diff options
5 files changed, 396 insertions, 333 deletions
diff --git a/services/core/java/com/android/server/media/AudioPlaybackMonitor.java b/services/core/java/com/android/server/media/AudioPlaybackMonitor.java deleted file mode 100644 index 791ee821b357..000000000000 --- a/services/core/java/com/android/server/media/AudioPlaybackMonitor.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright (C) 2017 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.server.media; - -import android.content.Context; -import android.media.AudioManager.AudioPlaybackCallback; -import android.media.AudioPlaybackConfiguration; -import android.media.IAudioService; -import android.media.IPlaybackConfigDispatcher; -import android.os.Binder; -import android.os.RemoteException; -import android.os.UserHandle; -import android.util.IntArray; -import android.util.Log; -import android.util.SparseArray; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Monitors changes in audio playback, and notify the newly started audio playback through the - * {@link OnAudioPlaybackStartedListener} and the activeness change through the - * {@link OnAudioPlaybackActiveStateListener}. - */ -class AudioPlaybackMonitor extends IPlaybackConfigDispatcher.Stub { - private static boolean DEBUG = MediaSessionService.DEBUG; - private static String TAG = "AudioPlaybackMonitor"; - - private static AudioPlaybackMonitor sInstance; - - /** - * Called when audio playback is started for a given UID. - */ - interface OnAudioPlaybackStartedListener { - void onAudioPlaybackStarted(int uid); - } - - /** - * Called when audio player state is changed. - */ - interface OnAudioPlayerActiveStateChangedListener { - void onAudioPlayerActiveStateChanged(int uid, boolean active); - } - - private final Object mLock = new Object(); - private final Context mContext; - private final List<OnAudioPlaybackStartedListener> mAudioPlaybackStartedListeners - = new ArrayList<>(); - private final List<OnAudioPlayerActiveStateChangedListener> - mAudioPlayerActiveStateChangedListeners = new ArrayList<>(); - private final Map<Integer, Integer> mAudioPlaybackStates = new HashMap<>(); - private final Set<Integer> mActiveAudioPlaybackClientUids = new HashSet<>(); - - // Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video) - // The UID whose audio playback becomes active at the last comes first. - // TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID. - private final IntArray mSortedAudioPlaybackClientUids = new IntArray(); - - static AudioPlaybackMonitor getInstance(Context context, IAudioService audioService) { - if (sInstance == null) { - sInstance = new AudioPlaybackMonitor(context, audioService); - } - return sInstance; - } - - private AudioPlaybackMonitor(Context context, IAudioService audioService) { - mContext = context; - try { - audioService.registerPlaybackCallback(this); - } catch (RemoteException e) { - Log.wtf(TAG, "Failed to register playback callback", e); - } - } - - /** - * Called when the {@link AudioPlaybackConfiguration} is updated. - * <p>If an app starts audio playback, the app's local media session will be the media button - * session. If the app has multiple media sessions, the playback active local session will be - * picked. - * - * @param configs List of the current audio playback configuration - */ - @Override - public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs, - boolean flush) { - if (flush) { - Binder.flushPendingCommands(); - } - final long token = Binder.clearCallingIdentity(); - try { - List<Integer> newActiveAudioPlaybackClientUids = new ArrayList<>(); - List<OnAudioPlayerActiveStateChangedListener> audioPlayerActiveStateChangedListeners; - List<OnAudioPlaybackStartedListener> audioPlaybackStartedListeners; - synchronized (mLock) { - // Update mActiveAudioPlaybackClientUids and mSortedAudioPlaybackClientUids, - // and find newly activated audio playbacks. - mActiveAudioPlaybackClientUids.clear(); - for (AudioPlaybackConfiguration config : configs) { - // Ignore inactive (i.e. not playing) or PLAYER_TYPE_JAM_SOUNDPOOL - // (i.e. playback from the SoundPool class which is only for sound effects) - // playback. - // Note that we shouldn't ignore PLAYER_TYPE_UNKNOWN because it might be OEM - // specific audio/video players. - if (!config.isActive() || config.getPlayerType() - == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { - continue; - } - - mActiveAudioPlaybackClientUids.add(config.getClientUid()); - Integer oldState = mAudioPlaybackStates.get(config.getPlayerInterfaceId()); - if (!isActiveState(oldState)) { - if (DEBUG) { - Log.d(TAG, "Found a new active media playback. " + - AudioPlaybackConfiguration.toLogFriendlyString(config)); - } - // New active audio playback. - newActiveAudioPlaybackClientUids.add(config.getClientUid()); - int index = mSortedAudioPlaybackClientUids.indexOf(config.getClientUid()); - if (index == 0) { - // It's the lastly played music app already. Skip updating. - continue; - } else if (index > 0) { - mSortedAudioPlaybackClientUids.remove(index); - } - mSortedAudioPlaybackClientUids.add(0, config.getClientUid()); - } - } - audioPlayerActiveStateChangedListeners = new ArrayList<>( - mAudioPlayerActiveStateChangedListeners); - audioPlaybackStartedListeners = new ArrayList<>(mAudioPlaybackStartedListeners); - } - // Notify the change of audio playback states. - for (AudioPlaybackConfiguration config : configs) { - boolean wasActive = isActiveState( - mAudioPlaybackStates.get(config.getPlayerInterfaceId())); - boolean isActive = config.isActive(); - if (wasActive != isActive) { - for (OnAudioPlayerActiveStateChangedListener listener - : audioPlayerActiveStateChangedListeners) { - listener.onAudioPlayerActiveStateChanged(config.getClientUid(), - isActive); - } - } - } - // Notify the start of audio playback - for (int uid : newActiveAudioPlaybackClientUids) { - for (OnAudioPlaybackStartedListener listener : audioPlaybackStartedListeners) { - listener.onAudioPlaybackStarted(uid); - } - } - mAudioPlaybackStates.clear(); - for (AudioPlaybackConfiguration config : configs) { - mAudioPlaybackStates.put(config.getPlayerInterfaceId(), config.getPlayerState()); - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Registers OnAudioPlaybackStartedListener. - */ - public void registerOnAudioPlaybackStartedListener(OnAudioPlaybackStartedListener listener) { - synchronized (mLock) { - mAudioPlaybackStartedListeners.add(listener); - } - } - - /** - * Unregisters OnAudioPlaybackStartedListener. - */ - public void unregisterOnAudioPlaybackStartedListener(OnAudioPlaybackStartedListener listener) { - synchronized (mLock) { - mAudioPlaybackStartedListeners.remove(listener); - } - } - - /** - * Registers OnAudioPlayerActiveStateChangedListener. - */ - public void registerOnAudioPlayerActiveStateChangedListener( - OnAudioPlayerActiveStateChangedListener listener) { - synchronized (mLock) { - mAudioPlayerActiveStateChangedListeners.add(listener); - } - } - - /** - * Unregisters OnAudioPlayerActiveStateChangedListener. - */ - public void unregisterOnAudioPlayerActiveStateChangedListener( - OnAudioPlayerActiveStateChangedListener listener) { - synchronized (mLock) { - mAudioPlayerActiveStateChangedListeners.remove(listener); - } - } - - /** - * Returns the sorted list of UIDs that have had active audio playback. (i.e. playing an - * audio/video) The UID whose audio playback becomes active at the last comes first. - */ - public IntArray getSortedAudioPlaybackClientUids() { - IntArray sortedAudioPlaybackClientUids = new IntArray(); - synchronized (mLock) { - sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids); - } - return sortedAudioPlaybackClientUids; - } - - /** - * Returns if the audio playback is active for the uid. - */ - public boolean isPlaybackActive(int uid) { - synchronized (mLock) { - return mActiveAudioPlaybackClientUids.contains(uid); - } - } - - /** - * Cleans up the sorted list of audio playback client UIDs with given {@param - * mediaButtonSessionUid}. - * <p>UIDs whose audio playback started after the media button session's audio playback - * cannot be the lastly played media app. So they won't needed anymore. - * - * @param mediaButtonSessionUid UID of the media button session. - */ - public void cleanUpAudioPlaybackUids(int mediaButtonSessionUid) { - synchronized (mLock) { - int userId = UserHandle.getUserId(mediaButtonSessionUid); - for (int i = mSortedAudioPlaybackClientUids.size() - 1; i >= 0; i--) { - if (mSortedAudioPlaybackClientUids.get(i) == mediaButtonSessionUid) { - break; - } - int uid = mSortedAudioPlaybackClientUids.get(i); - if (userId == UserHandle.getUserId(uid) && !isPlaybackActive(uid)) { - // Clean up unnecessary UIDs. - // It doesn't need to be managed profile aware because it's just to prevent - // the list from increasing indefinitely. The media button session updating - // shouldn't be affected by cleaning up. - mSortedAudioPlaybackClientUids.remove(i); - } - } - } - } - - /** - * Dumps {@link AudioPlaybackMonitor}. - */ - public void dump(PrintWriter pw, String prefix) { - synchronized (mLock) { - pw.println(prefix + "Audio playback (lastly played comes first)"); - String indent = prefix + " "; - for (int i = 0; i < mSortedAudioPlaybackClientUids.size(); i++) { - int uid = mSortedAudioPlaybackClientUids.get(i); - pw.print(indent + "uid=" + uid + " packages="); - String[] packages = mContext.getPackageManager().getPackagesForUid(uid); - if (packages != null && packages.length > 0) { - for (int j = 0; j < packages.length; j++) { - pw.print(packages[j] + " "); - } - } - pw.println(); - } - } - } - - private boolean isActiveState(Integer state) { - return state != null && state.equals(AudioPlaybackConfiguration.PLAYER_STATE_STARTED); - } -} diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java new file mode 100644 index 000000000000..110f26d5eddc --- /dev/null +++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2017 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.server.media; + +import android.annotation.Nullable; +import android.content.Context; +import android.media.AudioPlaybackConfiguration; +import android.media.IAudioService; +import android.media.IPlaybackConfigDispatcher; +import android.os.Binder; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.IntArray; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; + +import java.io.PrintWriter; +import java.util.HashSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Monitors the state changes of audio players. + */ +class AudioPlayerStateMonitor extends IPlaybackConfigDispatcher.Stub { + private static boolean DEBUG = MediaSessionService.DEBUG; + private static String TAG = "AudioPlayerStateMonitor"; + + private static AudioPlayerStateMonitor sInstance = new AudioPlayerStateMonitor(); + + /** + * Called when the state of audio player is changed. + */ + interface OnAudioPlayerStateChangedListener { + void onAudioPlayerStateChanged( + int uid, int prevState, @Nullable AudioPlaybackConfiguration config); + } + + private final static class MessageHandler extends Handler { + private static final int MSG_AUDIO_PLAYER_STATE_CHANGED = 1; + + private final OnAudioPlayerStateChangedListener mListsner; + + public MessageHandler(Looper looper, OnAudioPlayerStateChangedListener listener) { + super(looper); + mListsner = listener; + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_AUDIO_PLAYER_STATE_CHANGED: + mListsner.onAudioPlayerStateChanged( + msg.arg1, msg.arg2, (AudioPlaybackConfiguration) msg.obj); + break; + } + } + + public void sendAudioPlayerStateChangedMessage(int uid, int prevState, + AudioPlaybackConfiguration config) { + obtainMessage(MSG_AUDIO_PLAYER_STATE_CHANGED, uid, prevState, config).sendToTarget(); + } + } + + private final Object mLock = new Object(); + @GuardedBy("mLock") + private final Map<OnAudioPlayerStateChangedListener, MessageHandler> mListenerMap = + new HashMap<>(); + @GuardedBy("mLock") + private final Map<Integer, Integer> mAudioPlayerStates = new HashMap<>(); + @GuardedBy("mLock") + private final Map<Integer, HashSet<Integer>> mAudioPlayersForUid = new HashMap<>(); + // Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video) + // The UID whose audio playback becomes active at the last comes first. + // TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID. + @GuardedBy("mLock") + private final IntArray mSortedAudioPlaybackClientUids = new IntArray(); + + @GuardedBy("mLock") + private boolean mRegisteredToAudioService; + + static AudioPlayerStateMonitor getInstance() { + return sInstance; + } + + private AudioPlayerStateMonitor() { + } + + /** + * Called when the {@link AudioPlaybackConfiguration} is updated. + * <p>If an app starts audio playback, the app's local media session will be the media button + * session. If the app has multiple media sessions, the playback active local session will be + * picked. + * + * @param configs List of the current audio playback configuration + */ + @Override + public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs, + boolean flush) { + if (flush) { + Binder.flushPendingCommands(); + } + final long token = Binder.clearCallingIdentity(); + try { + final Map<Integer, Integer> prevAudioPlayerStates = new HashMap<>(mAudioPlayerStates); + final Map<Integer, HashSet<Integer>> prevAudioPlayersForUid = + new HashMap<>(mAudioPlayersForUid); + synchronized (mLock) { + mAudioPlayerStates.clear(); + mAudioPlayersForUid.clear(); + for (AudioPlaybackConfiguration config : configs) { + int pii = config.getPlayerInterfaceId(); + int uid = config.getClientUid(); + mAudioPlayerStates.put(pii, config.getPlayerState()); + HashSet<Integer> players = mAudioPlayersForUid.get(uid); + if (players == null) { + players = new HashSet<Integer>(); + players.add(pii); + mAudioPlayersForUid.put(uid, players); + } else { + players.add(pii); + } + } + for (AudioPlaybackConfiguration config : configs) { + if (!config.isActive()) { + continue; + } + + int uid = config.getClientUid(); + if (!isActiveState(prevAudioPlayerStates.get(config.getPlayerInterfaceId()))) { + if (DEBUG) { + Log.d(TAG, "Found a new active media playback. " + + AudioPlaybackConfiguration.toLogFriendlyString(config)); + } + // New active audio playback. + int index = mSortedAudioPlaybackClientUids.indexOf(uid); + if (index == 0) { + // It's the lastly played music app already. Skip updating. + continue; + } else if (index > 0) { + mSortedAudioPlaybackClientUids.remove(index); + } + mSortedAudioPlaybackClientUids.add(0, uid); + } + } + // Notify the change of audio player states. + for (AudioPlaybackConfiguration config : configs) { + Integer prevState = prevAudioPlayerStates.get(config.getPlayerInterfaceId()); + if (prevState == null || prevState != config.getPlayerState()) { + sendAudioPlayerStateChangedMessageLocked( + config.getClientUid(), prevState, config); + } + } + for (Integer prevUid : prevAudioPlayersForUid.keySet()) { + // If all players for prevUid is removed, notify the prev state was + // PLAYER_STATE_STARTED only when there were a player whose state was + // PLAYER_STATE_STARTED, otherwise any inactive state is okay to notify. + if (!mAudioPlayersForUid.containsKey(prevUid)) { + Set<Integer> players = mAudioPlayersForUid.get(prevUid); + int prevState = AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN; + for (int pii : players) { + Integer state = prevAudioPlayerStates.get(pii); + if (state == null) { + continue; + } + if (state == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + prevState = state; + break; + } else if (prevState + == AudioPlaybackConfiguration.PLAYER_STATE_UNKNOWN) { + prevState = state; + } + } + sendAudioPlayerStateChangedMessageLocked(prevUid, prevState, null); + } + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Registers OnAudioPlayerStateChangedListener. + */ + public void registerListener(OnAudioPlayerStateChangedListener listener, Handler handler) { + synchronized (mLock) { + mListenerMap.put(listener, new MessageHandler((handler == null) ? + Looper.myLooper() : handler.getLooper(), listener)); + } + } + + /** + * Unregisters OnAudioPlayerStateChangedListener. + */ + public void unregisterListener(OnAudioPlayerStateChangedListener listener) { + synchronized (mLock) { + mListenerMap.remove(listener); + } + } + + /** + * Returns the sorted list of UIDs that have had active audio playback. (i.e. playing an + * audio/video) The UID whose audio playback becomes active at the last comes first. + */ + public IntArray getSortedAudioPlaybackClientUids() { + IntArray sortedAudioPlaybackClientUids = new IntArray(); + synchronized (mLock) { + sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids); + } + return sortedAudioPlaybackClientUids; + } + + /** + * Returns if the audio playback is active for the uid. + */ + public boolean isPlaybackActive(int uid) { + synchronized (mLock) { + Set<Integer> players = mAudioPlayersForUid.get(uid); + if (players == null) { + return false; + } + for (Integer pii : players) { + if (isActiveState(mAudioPlayerStates.get(pii))) { + return true; + } + } + return false; + } + } + + /** + * Cleans up the sorted list of audio playback client UIDs with given {@param + * mediaButtonSessionUid}. + * <p>UIDs whose audio playback are inactive and have started before the media button session's + * audio playback cannot be the lastly played media app. So they won't needed anymore. + * + * @param mediaButtonSessionUid UID of the media button session. + */ + public void cleanUpAudioPlaybackUids(int mediaButtonSessionUid) { + synchronized (mLock) { + int userId = UserHandle.getUserId(mediaButtonSessionUid); + for (int i = mSortedAudioPlaybackClientUids.size() - 1; i >= 0; i--) { + if (mSortedAudioPlaybackClientUids.get(i) == mediaButtonSessionUid) { + break; + } + int uid = mSortedAudioPlaybackClientUids.get(i); + if (userId == UserHandle.getUserId(uid) && !isPlaybackActive(uid)) { + // Clean up unnecessary UIDs. + // It doesn't need to be managed profile aware because it's just to prevent + // the list from increasing indefinitely. The media button session updating + // shouldn't be affected by cleaning up. + mSortedAudioPlaybackClientUids.remove(i); + } + } + } + } + + /** + * Dumps {@link AudioPlayerStateMonitor}. + */ + public void dump(Context context, PrintWriter pw, String prefix) { + synchronized (mLock) { + pw.println(prefix + "Audio playback (lastly played comes first)"); + String indent = prefix + " "; + for (int i = 0; i < mSortedAudioPlaybackClientUids.size(); i++) { + int uid = mSortedAudioPlaybackClientUids.get(i); + pw.print(indent + "uid=" + uid + " packages="); + String[] packages = context.getPackageManager().getPackagesForUid(uid); + if (packages != null && packages.length > 0) { + for (int j = 0; j < packages.length; j++) { + pw.print(packages[j] + " "); + } + } + pw.println(); + } + } + } + + public void registerSelfIntoAudioServiceIfNeeded(IAudioService audioService) { + synchronized (mLock) { + try { + if (!mRegisteredToAudioService) { + audioService.registerPlaybackCallback(this); + mRegisteredToAudioService = true; + } + } catch (RemoteException e) { + Log.wtf(TAG, "Failed to register playback callback", e); + mRegisteredToAudioService = false; + } + } + } + + private void sendAudioPlayerStateChangedMessageLocked( + final int uid, final int prevState, final AudioPlaybackConfiguration config) { + for (MessageHandler messageHandler : mListenerMap.values()) { + messageHandler.sendAudioPlayerStateChangedMessage(uid, prevState, config); + } + } + + private static boolean isActiveState(Integer state) { + return state != null && state.equals(AudioPlaybackConfiguration.PLAYER_STATE_STARTED); + } +} diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 1cfd5f02e810..3c9e1d456c6f 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -19,12 +19,14 @@ package com.android.server.media; import com.android.internal.util.DumpUtils; import com.android.server.Watchdog; +import android.annotation.Nullable; import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.media.AudioPlaybackConfiguration; import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; @@ -96,7 +98,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub private int mCurrentUserId = -1; private boolean mGlobalBluetoothA2dpOn = false; private final IAudioService mAudioService; - private final AudioPlaybackMonitor mAudioPlaybackMonitor; + private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; + private final Handler mHandler = new Handler(); private final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); public MediaRouterService(Context context) { @@ -106,31 +109,57 @@ public final class MediaRouterService extends IMediaRouterService.Stub mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); - mAudioPlaybackMonitor = AudioPlaybackMonitor.getInstance(context, mAudioService); - mAudioPlaybackMonitor.registerOnAudioPlayerActiveStateChangedListener( - new AudioPlaybackMonitor.OnAudioPlayerActiveStateChangedListener() { + mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(); + mAudioPlayerStateMonitor.registerListener( + new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() { + static final long WAIT_MS = 500; + final Runnable mRestoreBluetoothA2dpRunnable = new Runnable() { + @Override + public void run() { + restoreBluetoothA2dp(); + } + }; + @Override - public void onAudioPlayerActiveStateChanged(int uid, boolean active) { + public void onAudioPlayerStateChanged( + int uid, int prevState, @Nullable AudioPlaybackConfiguration config) { + int restoreUid = -1; + boolean active = config == null ? false : config.isActive(); if (active) { - restoreRoute(uid); + restoreUid = uid; + } else if (prevState != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + // Noting to do if the prev state is not an active state. + return; } else { IntArray sortedAudioPlaybackClientUids = - mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids(); - boolean restored = false; - for (int i = 0; i < sortedAudioPlaybackClientUids.size(); i++) { - if (mAudioPlaybackMonitor.isPlaybackActive( + mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); + for (int i = 0; i < sortedAudioPlaybackClientUids.size(); ++i) { + if (mAudioPlayerStateMonitor.isPlaybackActive( sortedAudioPlaybackClientUids.get(i))) { - restoreRoute(sortedAudioPlaybackClientUids.get(i)); - restored = true; + restoreUid = sortedAudioPlaybackClientUids.get(i); break; } } - if (!restored) { - restoreBluetoothA2dp(); + } + + mHandler.removeCallbacks(mRestoreBluetoothA2dpRunnable); + if (restoreUid >= 0) { + restoreRoute(restoreUid); + if (DEBUG) { + Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid + + " active " + active + " restoring " + restoreUid); + } + } else { + mHandler.postDelayed(mRestoreBluetoothA2dpRunnable, WAIT_MS); + if (DEBUG) { + Slog.d(TAG, "onAudioPlayerStateChanged: " + "uid " + uid + + " active " + active + " delaying"); } } } - }); + }, mHandler); + mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService); + AudioRoutesInfo audioRoutes = null; try { audioRoutes = mAudioService.startWatchingRoutes(new IAudioRoutesObserver.Stub() { @@ -261,9 +290,14 @@ public final class MediaRouterService extends IMediaRouterService.Stub final long token = Binder.clearCallingIdentity(); try { + ClientRecord clientRecord; synchronized (mLock) { - return isPlaybackActiveLocked(client); + clientRecord = mAllClientRecords.get(client.asBinder()); + } + if (clientRecord != null) { + return mAudioPlayerStateMonitor.isPlaybackActive(clientRecord.mUid); } + return false; } finally { Binder.restoreCallingIdentity(token); } @@ -480,14 +514,6 @@ public final class MediaRouterService extends IMediaRouterService.Stub return null; } - private boolean isPlaybackActiveLocked(IMediaRouterClient client) { - ClientRecord clientRecord = mAllClientRecords.get(client.asBinder()); - if (clientRecord != null) { - return mAudioPlaybackMonitor.isPlaybackActive(clientRecord.mUid); - } - return false; - } - private void setDiscoveryRequestLocked(IMediaRouterClient client, int routeTypes, boolean activeScan) { final IBinder binder = client.asBinder(); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index aa652445c45a..f6a81d07277a 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -16,6 +16,7 @@ package com.android.server.media; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.INotificationManager; import android.app.KeyguardManager; @@ -31,7 +32,7 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.media.AudioManager; -import android.media.AudioManagerInternal; +import android.media.AudioPlaybackConfiguration; import android.media.AudioSystem; import android.media.IAudioService; import android.media.IRemoteVolumeController; @@ -68,7 +69,6 @@ import android.view.KeyEvent; import android.view.ViewConfiguration; import com.android.internal.util.DumpUtils; -import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.Watchdog; import com.android.server.Watchdog.Monitor; @@ -104,7 +104,6 @@ public class MediaSessionService extends SystemService implements Monitor { private KeyguardManager mKeyguardManager; private IAudioService mAudioService; - private AudioManagerInternal mAudioManagerInternal; private ContentResolver mContentResolver; private SettingsObserver mSettingsObserver; private INotificationManager mNotificationManager; @@ -114,7 +113,7 @@ public class MediaSessionService extends SystemService implements Monitor { // It's always not null after the MediaSessionService is started. private FullUserRecord mCurrentFullUserRecord; private MediaSessionRecord mGlobalPrioritySession; - private AudioPlaybackMonitor mAudioPlaybackMonitor; + private AudioPlayerStateMonitor mAudioPlayerStateMonitor; // Used to notify system UI when remote volume was changed. TODO find a // better way to handle this. @@ -137,11 +136,16 @@ public class MediaSessionService extends SystemService implements Monitor { mKeyguardManager = (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); mAudioService = getAudioService(); - mAudioPlaybackMonitor = AudioPlaybackMonitor.getInstance(getContext(), mAudioService); - mAudioPlaybackMonitor.registerOnAudioPlaybackStartedListener( - new AudioPlaybackMonitor.OnAudioPlaybackStartedListener() { + mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(); + mAudioPlayerStateMonitor.registerListener( + new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() { @Override - public void onAudioPlaybackStarted(int uid) { + public void onAudioPlayerStateChanged( + int uid, int prevState, @Nullable AudioPlaybackConfiguration config) { + if (config == null || !config.isActive() || config.getPlayerType() + == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { + return; + } synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(UserHandle.getUserId(uid)); @@ -150,8 +154,8 @@ public class MediaSessionService extends SystemService implements Monitor { } } } - }); - mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); + }, null /* handler */); + mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService); mContentResolver = getContext().getContentResolver(); mSettingsObserver = new SettingsObserver(); mSettingsObserver.observe(); @@ -650,7 +654,7 @@ public class MediaSessionService extends SystemService implements Monitor { public FullUserRecord(int fullUserId) { mFullUserId = fullUserId; - mPriorityStack = new MediaSessionStack(mAudioPlaybackMonitor, this); + mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this); // Restore the remembered media button receiver before the boot. String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId); @@ -1309,7 +1313,7 @@ public class MediaSessionService extends SystemService implements Monitor { for (int i = 0; i < count; i++) { mUserRecords.valueAt(i).dumpLocked(pw, ""); } - mAudioPlaybackMonitor.dump(pw, ""); + mAudioPlayerStateMonitor.dump(getContext(), pw, ""); } } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index d9fe72e0c8bb..719ec362e6e8 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -75,7 +75,7 @@ class MediaSessionStack { */ private final List<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); - private final AudioPlaybackMonitor mAudioPlaybackMonitor; + private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener; /** @@ -84,7 +84,6 @@ class MediaSessionStack { */ private MediaSessionRecord mMediaButtonSession; - private MediaSessionRecord mCachedDefault; private MediaSessionRecord mCachedVolumeDefault; /** @@ -93,8 +92,8 @@ class MediaSessionStack { private final SparseArray<ArrayList<MediaSessionRecord>> mCachedActiveLists = new SparseArray<>(); - MediaSessionStack(AudioPlaybackMonitor monitor, OnMediaButtonSessionChangedListener listener) { - mAudioPlaybackMonitor = monitor; + MediaSessionStack(AudioPlayerStateMonitor monitor, OnMediaButtonSessionChangedListener listener) { + mAudioPlayerStateMonitor = monitor; mOnMediaButtonSessionChangedListener = listener; } @@ -187,13 +186,13 @@ class MediaSessionStack { if (DEBUG) { Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2)); } - IntArray audioPlaybackUids = mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids(); + IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); for (int i = 0; i < audioPlaybackUids.size(); i++) { MediaSessionRecord mediaButtonSession = findMediaButtonSession(audioPlaybackUids.get(i)); if (mediaButtonSession != null) { // Found the media button session. - mAudioPlaybackMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid()); + mAudioPlayerStateMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid()); if (mMediaButtonSession != mediaButtonSession) { updateMediaButtonSession(mediaButtonSession); } @@ -216,7 +215,7 @@ class MediaSessionStack { for (MediaSessionRecord session : mSessions) { if (uid == session.getUid()) { if (session.getPlaybackState() != null && session.isPlaybackActive() == - mAudioPlaybackMonitor.isPlaybackActive(session.getUid())) { + mAudioPlayerStateMonitor.isPlaybackActive(session.getUid())) { // If there's a media session whose PlaybackState matches // the audio playback state, return it immediately. return session; @@ -376,7 +375,6 @@ class MediaSessionStack { } private void clearCache(int userId) { - mCachedDefault = null; mCachedVolumeDefault = null; mCachedActiveLists.remove(userId); // mCachedActiveLists may also include the list of sessions for UserHandle.USER_ALL, |