diff --git a/services/core/java/com/android/server/media/AudioPlaybackMonitor.java b/services/core/java/com/android/server/media/AudioPlaybackMonitor.java new file mode 100644 index 0000000000000..791ee821b357d --- /dev/null +++ b/services/core/java/com/android/server/media/AudioPlaybackMonitor.java @@ -0,0 +1,289 @@ +/* + * 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 mAudioPlaybackStartedListeners + = new ArrayList<>(); + private final List + mAudioPlayerActiveStateChangedListeners = new ArrayList<>(); + private final Map mAudioPlaybackStates = new HashMap<>(); + private final Set 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. + *

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 configs, + boolean flush) { + if (flush) { + Binder.flushPendingCommands(); + } + final long token = Binder.clearCallingIdentity(); + try { + List newActiveAudioPlaybackClientUids = new ArrayList<>(); + List audioPlayerActiveStateChangedListeners; + List 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}. + *

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 deleted file mode 100644 index 110f26d5eddc7..0000000000000 --- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java +++ /dev/null @@ -1,324 +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.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 mListenerMap = - new HashMap<>(); - @GuardedBy("mLock") - private final Map mAudioPlayerStates = new HashMap<>(); - @GuardedBy("mLock") - private final Map> 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. - *

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 configs, - boolean flush) { - if (flush) { - Binder.flushPendingCommands(); - } - final long token = Binder.clearCallingIdentity(); - try { - final Map prevAudioPlayerStates = new HashMap<>(mAudioPlayerStates); - final Map> 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 players = mAudioPlayersForUid.get(uid); - if (players == null) { - players = new HashSet(); - 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 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 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}. - *

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 3c9e1d456c6fd..1cfd5f02e8105 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -19,14 +19,12 @@ 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; @@ -98,8 +96,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub private int mCurrentUserId = -1; private boolean mGlobalBluetoothA2dpOn = false; private final IAudioService mAudioService; - private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; - private final Handler mHandler = new Handler(); + private final AudioPlaybackMonitor mAudioPlaybackMonitor; private final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); public MediaRouterService(Context context) { @@ -109,57 +106,31 @@ public final class MediaRouterService extends IMediaRouterService.Stub mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); - mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(); - mAudioPlayerStateMonitor.registerListener( - new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() { - static final long WAIT_MS = 500; - final Runnable mRestoreBluetoothA2dpRunnable = new Runnable() { - @Override - public void run() { - restoreBluetoothA2dp(); - } - }; - + mAudioPlaybackMonitor = AudioPlaybackMonitor.getInstance(context, mAudioService); + mAudioPlaybackMonitor.registerOnAudioPlayerActiveStateChangedListener( + new AudioPlaybackMonitor.OnAudioPlayerActiveStateChangedListener() { @Override - public void onAudioPlayerStateChanged( - int uid, int prevState, @Nullable AudioPlaybackConfiguration config) { - int restoreUid = -1; - boolean active = config == null ? false : config.isActive(); + public void onAudioPlayerActiveStateChanged(int uid, boolean active) { if (active) { - restoreUid = uid; - } else if (prevState != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { - // Noting to do if the prev state is not an active state. - return; + restoreRoute(uid); } else { IntArray sortedAudioPlaybackClientUids = - mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); - for (int i = 0; i < sortedAudioPlaybackClientUids.size(); ++i) { - if (mAudioPlayerStateMonitor.isPlaybackActive( + mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids(); + boolean restored = false; + for (int i = 0; i < sortedAudioPlaybackClientUids.size(); i++) { + if (mAudioPlaybackMonitor.isPlaybackActive( sortedAudioPlaybackClientUids.get(i))) { - restoreUid = sortedAudioPlaybackClientUids.get(i); + restoreRoute(sortedAudioPlaybackClientUids.get(i)); + restored = true; break; } } - } - - 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"); + if (!restored) { + restoreBluetoothA2dp(); } } } - }, mHandler); - mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService); - + }); AudioRoutesInfo audioRoutes = null; try { audioRoutes = mAudioService.startWatchingRoutes(new IAudioRoutesObserver.Stub() { @@ -290,14 +261,9 @@ public final class MediaRouterService extends IMediaRouterService.Stub final long token = Binder.clearCallingIdentity(); try { - ClientRecord clientRecord; synchronized (mLock) { - clientRecord = mAllClientRecords.get(client.asBinder()); + return isPlaybackActiveLocked(client); } - if (clientRecord != null) { - return mAudioPlayerStateMonitor.isPlaybackActive(clientRecord.mUid); - } - return false; } finally { Binder.restoreCallingIdentity(token); } @@ -514,6 +480,14 @@ 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 f6a81d07277a5..aa652445c45a1 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -16,7 +16,6 @@ package com.android.server.media; -import android.annotation.Nullable; import android.app.ActivityManager; import android.app.INotificationManager; import android.app.KeyguardManager; @@ -32,7 +31,7 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.media.AudioManager; -import android.media.AudioPlaybackConfiguration; +import android.media.AudioManagerInternal; import android.media.AudioSystem; import android.media.IAudioService; import android.media.IRemoteVolumeController; @@ -69,6 +68,7 @@ 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,6 +104,7 @@ 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; @@ -113,7 +114,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 AudioPlayerStateMonitor mAudioPlayerStateMonitor; + private AudioPlaybackMonitor mAudioPlaybackMonitor; // Used to notify system UI when remote volume was changed. TODO find a // better way to handle this. @@ -136,16 +137,11 @@ public class MediaSessionService extends SystemService implements Monitor { mKeyguardManager = (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE); mAudioService = getAudioService(); - mAudioPlayerStateMonitor = AudioPlayerStateMonitor.getInstance(); - mAudioPlayerStateMonitor.registerListener( - new AudioPlayerStateMonitor.OnAudioPlayerStateChangedListener() { + mAudioPlaybackMonitor = AudioPlaybackMonitor.getInstance(getContext(), mAudioService); + mAudioPlaybackMonitor.registerOnAudioPlaybackStartedListener( + new AudioPlaybackMonitor.OnAudioPlaybackStartedListener() { @Override - public void onAudioPlayerStateChanged( - int uid, int prevState, @Nullable AudioPlaybackConfiguration config) { - if (config == null || !config.isActive() || config.getPlayerType() - == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { - return; - } + public void onAudioPlaybackStarted(int uid) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(UserHandle.getUserId(uid)); @@ -154,8 +150,8 @@ public class MediaSessionService extends SystemService implements Monitor { } } } - }, null /* handler */); - mAudioPlayerStateMonitor.registerSelfIntoAudioServiceIfNeeded(mAudioService); + }); + mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class); mContentResolver = getContext().getContentResolver(); mSettingsObserver = new SettingsObserver(); mSettingsObserver.observe(); @@ -654,7 +650,7 @@ public class MediaSessionService extends SystemService implements Monitor { public FullUserRecord(int fullUserId) { mFullUserId = fullUserId; - mPriorityStack = new MediaSessionStack(mAudioPlayerStateMonitor, this); + mPriorityStack = new MediaSessionStack(mAudioPlaybackMonitor, this); // Restore the remembered media button receiver before the boot. String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId); @@ -1313,7 +1309,7 @@ public class MediaSessionService extends SystemService implements Monitor { for (int i = 0; i < count; i++) { mUserRecords.valueAt(i).dumpLocked(pw, ""); } - mAudioPlayerStateMonitor.dump(getContext(), pw, ""); + mAudioPlaybackMonitor.dump(pw, ""); } } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 719ec362e6e8c..d9fe72e0c8bbe 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 mSessions = new ArrayList(); - private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; + private final AudioPlaybackMonitor mAudioPlaybackMonitor; private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener; /** @@ -84,6 +84,7 @@ class MediaSessionStack { */ private MediaSessionRecord mMediaButtonSession; + private MediaSessionRecord mCachedDefault; private MediaSessionRecord mCachedVolumeDefault; /** @@ -92,8 +93,8 @@ class MediaSessionStack { private final SparseArray> mCachedActiveLists = new SparseArray<>(); - MediaSessionStack(AudioPlayerStateMonitor monitor, OnMediaButtonSessionChangedListener listener) { - mAudioPlayerStateMonitor = monitor; + MediaSessionStack(AudioPlaybackMonitor monitor, OnMediaButtonSessionChangedListener listener) { + mAudioPlaybackMonitor = monitor; mOnMediaButtonSessionChangedListener = listener; } @@ -186,13 +187,13 @@ class MediaSessionStack { if (DEBUG) { Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2)); } - IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); + IntArray audioPlaybackUids = mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids(); for (int i = 0; i < audioPlaybackUids.size(); i++) { MediaSessionRecord mediaButtonSession = findMediaButtonSession(audioPlaybackUids.get(i)); if (mediaButtonSession != null) { // Found the media button session. - mAudioPlayerStateMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid()); + mAudioPlaybackMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid()); if (mMediaButtonSession != mediaButtonSession) { updateMediaButtonSession(mediaButtonSession); } @@ -215,7 +216,7 @@ class MediaSessionStack { for (MediaSessionRecord session : mSessions) { if (uid == session.getUid()) { if (session.getPlaybackState() != null && session.isPlaybackActive() == - mAudioPlayerStateMonitor.isPlaybackActive(session.getUid())) { + mAudioPlaybackMonitor.isPlaybackActive(session.getUid())) { // If there's a media session whose PlaybackState matches // the audio playback state, return it immediately. return session; @@ -375,6 +376,7 @@ 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,