Loading packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.kt +43 −21 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.content.Intent import android.content.pm.PackageManager import android.media.MediaMetadata import android.media.session.MediaController import android.media.session.MediaController.PlaybackInfo import android.media.session.MediaSession import android.media.session.MediaSessionManager import android.media.session.PlaybackState Loading Loading @@ -98,16 +99,22 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { } /** Set volume `level` to remote media `token` */ fun setVolume(token: MediaSession.Token, level: Int) { fun setVolume(sessionId: SessionId, volumeLevel: Int) { when (sessionId) { is SessionId.Media -> setMediaSessionVolume(sessionId.token, volumeLevel) } } private fun setMediaSessionVolume(token: MediaSession.Token, volumeLevel: Int) { val record = mRecords[token] if (record == null) { Log.w(TAG, "setVolume: No record found for token $token") return } if (D.BUG) { Log.d(TAG, "Setting level to $level") Log.d(TAG, "Setting level to $volumeLevel") } record.controller.setVolumeTo(level, 0) record.controller.setVolumeTo(volumeLevel, 0) } private fun onRemoteVolumeChangedH(sessionToken: MediaSession.Token, flags: Int) { Loading @@ -122,7 +129,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { ) } val token = controller.sessionToken mCallbacks.onRemoteVolumeChanged(token, flags) mCallbacks.onRemoteVolumeChanged(SessionId.from(token), flags) } private fun onUpdateRemoteSessionListH(sessionToken: MediaSession.Token?) { Loading Loading @@ -158,7 +165,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { controller.registerCallback(record, mHandler) } val record = mRecords[token] val remote = isRemote(playbackInfo) val remote = playbackInfo.isRemote() if (remote) { updateRemoteH(token, record!!.name, playbackInfo) record.sentRemote = true Loading @@ -172,7 +179,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { Log.d(TAG, "Removing " + record.name + " sentRemote=" + record.sentRemote) } if (record.sentRemote) { mCallbacks.onRemoteRemoved(token) mCallbacks.onRemoteRemoved(SessionId.from(token)) record.sentRemote = false } } Loading Loading @@ -213,8 +220,8 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { private fun updateRemoteH( token: MediaSession.Token, name: String?, pi: MediaController.PlaybackInfo, ) = mCallbacks.onRemoteUpdate(token, name, pi) playbackInfo: PlaybackInfo, ) = mCallbacks.onRemoteUpdate(SessionId.from(token), name, VolumeInfo.from(playbackInfo)) private inner class MediaControllerRecord(val controller: MediaController) : MediaController.Callback() { Loading @@ -225,7 +232,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { return method + " " + controller.packageName + " " } override fun onAudioInfoChanged(info: MediaController.PlaybackInfo) { override fun onAudioInfoChanged(info: PlaybackInfo) { if (D.BUG) { Log.d( TAG, Loading @@ -235,9 +242,9 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { sentRemote), ) } val remote = isRemote(info) val remote = info.isRemote() if (!remote && sentRemote) { mCallbacks.onRemoteRemoved(controller.sessionToken) mCallbacks.onRemoteRemoved(SessionId.from(controller.sessionToken)) sentRemote = false } else if (remote) { updateRemoteH(controller.sessionToken, name, info) Loading Loading @@ -301,20 +308,36 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { } } /** Opaque id for ongoing sessions that support volume adjustment. */ sealed interface SessionId { companion object { fun from(token: MediaSession.Token) = Media(token) } data class Media(val token: MediaSession.Token) : SessionId } /** Holds session volume information. */ data class VolumeInfo(val currentVolume: Int, val maxVolume: Int) { companion object { fun from(playbackInfo: PlaybackInfo) = VolumeInfo(playbackInfo.currentVolume, playbackInfo.maxVolume) } } /** Callback for remote media sessions */ interface Callbacks { /** Invoked when remote media session is updated */ fun onRemoteUpdate( token: MediaSession.Token?, name: String?, pi: MediaController.PlaybackInfo?, ) fun onRemoteUpdate(token: SessionId?, name: String?, volumeInfo: VolumeInfo?) /** Invoked when remote media session is removed */ fun onRemoteRemoved(token: MediaSession.Token?) fun onRemoteRemoved(token: SessionId?) /** Invoked when remote volume is changed */ fun onRemoteVolumeChanged(token: MediaSession.Token?, flags: Int) fun onRemoteVolumeChanged(token: SessionId?, flags: Int) } companion object { Loading @@ -325,12 +348,11 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { const val UPDATE_REMOTE_SESSION_LIST: Int = 3 private const val USE_SERVICE_LABEL = false private fun isRemote(pi: MediaController.PlaybackInfo?): Boolean = pi != null && pi.playbackType == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE } } private fun PlaybackInfo?.isRemote() = this?.playbackType == PlaybackInfo.PLAYBACK_TYPE_REMOTE private fun MediaController.dump(n: Int, writer: PrintWriter) { writer.println(" Controller $n: $packageName") Loading packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +5 −2 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.TestScopeProvider; import com.android.settingslib.volume.MediaSessions; import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCaseExtKt; import com.android.systemui.broadcast.BroadcastDispatcher; Loading Loading @@ -268,13 +269,15 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { @Test public void testOnRemoteVolumeChanged_newStream_noNullPointer() { MediaSession.Token token = new MediaSession.Token(Process.myUid(), null); mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(token, 0); var sessionId = MediaSessions.SessionId.Companion.from(token); mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(sessionId, 0); } @Test public void testOnRemoteRemove_newStream_noNullPointer() { MediaSession.Token token = new MediaSession.Token(Process.myUid(), null); mVolumeController.mMediaSessionsCallbacksW.onRemoteRemoved(token); var sessionId = MediaSessions.SessionId.Companion.from(token); mVolumeController.mMediaSessionsCallbacksW.onRemoteRemoved(sessionId); } @Test Loading packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +17 −16 Original line number Diff line number Diff line Loading @@ -37,8 +37,6 @@ import android.media.IAudioService; import android.media.IVolumeController; import android.media.MediaRouter2Manager; import android.media.VolumePolicy; import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession.Token; import android.net.Uri; import android.os.Handler; import android.os.HandlerExecutor; Loading @@ -61,6 +59,7 @@ import androidx.lifecycle.Observer; import com.android.internal.annotations.GuardedBy; import com.android.settingslib.volume.MediaSessions; import com.android.settingslib.volume.MediaSessions.SessionId; import com.android.systemui.Dumpable; import com.android.systemui.Flags; import com.android.systemui.broadcast.BroadcastDispatcher; Loading Loading @@ -1402,12 +1401,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } protected final class MediaSessionsCallbacks implements MediaSessions.Callbacks { private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>(); private final HashMap<SessionId, Integer> mRemoteStreams = new HashMap<>(); private int mNextStream = DYNAMIC_STREAM_REMOTE_START_INDEX; @Override public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) { public void onRemoteUpdate( SessionId token, String name, MediaSessions.VolumeInfo volumeInfo) { addStream(token, "onRemoteUpdate"); int stream = 0; Loading @@ -1415,14 +1415,15 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa stream = mRemoteStreams.get(token); } Slog.d(TAG, "onRemoteUpdate: stream: " + stream + " volume: " + pi.getCurrentVolume()); "onRemoteUpdate: stream: " + stream + " volume: " + volumeInfo.getCurrentVolume()); boolean changed = mState.states.indexOfKey(stream) < 0; final StreamState ss = streamStateW(stream); ss.dynamic = true; ss.levelMin = 0; ss.levelMax = pi.getMaxVolume(); if (ss.level != pi.getCurrentVolume()) { ss.level = pi.getCurrentVolume(); ss.levelMax = volumeInfo.getMaxVolume(); if (ss.level != volumeInfo.getCurrentVolume()) { ss.level = volumeInfo.getCurrentVolume(); changed = true; } if (!Objects.equals(ss.remoteLabel, name)) { Loading @@ -1437,11 +1438,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } @Override public void onRemoteVolumeChanged(Token token, int flags) { addStream(token, "onRemoteVolumeChanged"); public void onRemoteVolumeChanged(SessionId sessionId, int flags) { addStream(sessionId, "onRemoteVolumeChanged"); int stream = 0; synchronized (mRemoteStreams) { stream = mRemoteStreams.get(token); stream = mRemoteStreams.get(sessionId); } final boolean showUI = shouldShowUI(flags); Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI); Loading @@ -1459,7 +1460,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } @Override public void onRemoteRemoved(Token token) { public void onRemoteRemoved(SessionId token) { int stream; synchronized (mRemoteStreams) { if (!mRemoteStreams.containsKey(token)) { Loading @@ -1480,7 +1481,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } public void setStreamVolume(int stream, int level) { final Token token = findToken(stream); final SessionId token = findToken(stream); if (token == null) { Log.w(TAG, "setStreamVolume: No token found for stream: " + stream); return; Loading @@ -1488,9 +1489,9 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mMediaSessions.setVolume(token, level); } private Token findToken(int stream) { private SessionId findToken(int stream) { synchronized (mRemoteStreams) { for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) { for (Map.Entry<SessionId, Integer> entry : mRemoteStreams.entrySet()) { if (entry.getValue().equals(stream)) { return entry.getKey(); } Loading @@ -1499,7 +1500,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa return null; } private void addStream(Token token, String triggeringMethod) { private void addStream(SessionId token, String triggeringMethod) { synchronized (mRemoteStreams) { if (!mRemoteStreams.containsKey(token)) { mRemoteStreams.put(token, mNextStream); Loading Loading
packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.kt +43 −21 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.content.Intent import android.content.pm.PackageManager import android.media.MediaMetadata import android.media.session.MediaController import android.media.session.MediaController.PlaybackInfo import android.media.session.MediaSession import android.media.session.MediaSessionManager import android.media.session.PlaybackState Loading Loading @@ -98,16 +99,22 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { } /** Set volume `level` to remote media `token` */ fun setVolume(token: MediaSession.Token, level: Int) { fun setVolume(sessionId: SessionId, volumeLevel: Int) { when (sessionId) { is SessionId.Media -> setMediaSessionVolume(sessionId.token, volumeLevel) } } private fun setMediaSessionVolume(token: MediaSession.Token, volumeLevel: Int) { val record = mRecords[token] if (record == null) { Log.w(TAG, "setVolume: No record found for token $token") return } if (D.BUG) { Log.d(TAG, "Setting level to $level") Log.d(TAG, "Setting level to $volumeLevel") } record.controller.setVolumeTo(level, 0) record.controller.setVolumeTo(volumeLevel, 0) } private fun onRemoteVolumeChangedH(sessionToken: MediaSession.Token, flags: Int) { Loading @@ -122,7 +129,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { ) } val token = controller.sessionToken mCallbacks.onRemoteVolumeChanged(token, flags) mCallbacks.onRemoteVolumeChanged(SessionId.from(token), flags) } private fun onUpdateRemoteSessionListH(sessionToken: MediaSession.Token?) { Loading Loading @@ -158,7 +165,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { controller.registerCallback(record, mHandler) } val record = mRecords[token] val remote = isRemote(playbackInfo) val remote = playbackInfo.isRemote() if (remote) { updateRemoteH(token, record!!.name, playbackInfo) record.sentRemote = true Loading @@ -172,7 +179,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { Log.d(TAG, "Removing " + record.name + " sentRemote=" + record.sentRemote) } if (record.sentRemote) { mCallbacks.onRemoteRemoved(token) mCallbacks.onRemoteRemoved(SessionId.from(token)) record.sentRemote = false } } Loading Loading @@ -213,8 +220,8 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { private fun updateRemoteH( token: MediaSession.Token, name: String?, pi: MediaController.PlaybackInfo, ) = mCallbacks.onRemoteUpdate(token, name, pi) playbackInfo: PlaybackInfo, ) = mCallbacks.onRemoteUpdate(SessionId.from(token), name, VolumeInfo.from(playbackInfo)) private inner class MediaControllerRecord(val controller: MediaController) : MediaController.Callback() { Loading @@ -225,7 +232,7 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { return method + " " + controller.packageName + " " } override fun onAudioInfoChanged(info: MediaController.PlaybackInfo) { override fun onAudioInfoChanged(info: PlaybackInfo) { if (D.BUG) { Log.d( TAG, Loading @@ -235,9 +242,9 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { sentRemote), ) } val remote = isRemote(info) val remote = info.isRemote() if (!remote && sentRemote) { mCallbacks.onRemoteRemoved(controller.sessionToken) mCallbacks.onRemoteRemoved(SessionId.from(controller.sessionToken)) sentRemote = false } else if (remote) { updateRemoteH(controller.sessionToken, name, info) Loading Loading @@ -301,20 +308,36 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { } } /** Opaque id for ongoing sessions that support volume adjustment. */ sealed interface SessionId { companion object { fun from(token: MediaSession.Token) = Media(token) } data class Media(val token: MediaSession.Token) : SessionId } /** Holds session volume information. */ data class VolumeInfo(val currentVolume: Int, val maxVolume: Int) { companion object { fun from(playbackInfo: PlaybackInfo) = VolumeInfo(playbackInfo.currentVolume, playbackInfo.maxVolume) } } /** Callback for remote media sessions */ interface Callbacks { /** Invoked when remote media session is updated */ fun onRemoteUpdate( token: MediaSession.Token?, name: String?, pi: MediaController.PlaybackInfo?, ) fun onRemoteUpdate(token: SessionId?, name: String?, volumeInfo: VolumeInfo?) /** Invoked when remote media session is removed */ fun onRemoteRemoved(token: MediaSession.Token?) fun onRemoteRemoved(token: SessionId?) /** Invoked when remote volume is changed */ fun onRemoteVolumeChanged(token: MediaSession.Token?, flags: Int) fun onRemoteVolumeChanged(token: SessionId?, flags: Int) } companion object { Loading @@ -325,12 +348,11 @@ class MediaSessions(context: Context, looper: Looper, callbacks: Callbacks) { const val UPDATE_REMOTE_SESSION_LIST: Int = 3 private const val USE_SERVICE_LABEL = false private fun isRemote(pi: MediaController.PlaybackInfo?): Boolean = pi != null && pi.playbackType == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE } } private fun PlaybackInfo?.isRemote() = this?.playbackType == PlaybackInfo.PLAYBACK_TYPE_REMOTE private fun MediaController.dump(n: Int, writer: PrintWriter) { writer.println(" Controller $n: $packageName") Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +5 −2 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.TestScopeProvider; import com.android.settingslib.volume.MediaSessions; import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCaseExtKt; import com.android.systemui.broadcast.BroadcastDispatcher; Loading Loading @@ -268,13 +269,15 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { @Test public void testOnRemoteVolumeChanged_newStream_noNullPointer() { MediaSession.Token token = new MediaSession.Token(Process.myUid(), null); mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(token, 0); var sessionId = MediaSessions.SessionId.Companion.from(token); mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(sessionId, 0); } @Test public void testOnRemoteRemove_newStream_noNullPointer() { MediaSession.Token token = new MediaSession.Token(Process.myUid(), null); mVolumeController.mMediaSessionsCallbacksW.onRemoteRemoved(token); var sessionId = MediaSessions.SessionId.Companion.from(token); mVolumeController.mMediaSessionsCallbacksW.onRemoteRemoved(sessionId); } @Test Loading
packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +17 −16 Original line number Diff line number Diff line Loading @@ -37,8 +37,6 @@ import android.media.IAudioService; import android.media.IVolumeController; import android.media.MediaRouter2Manager; import android.media.VolumePolicy; import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession.Token; import android.net.Uri; import android.os.Handler; import android.os.HandlerExecutor; Loading @@ -61,6 +59,7 @@ import androidx.lifecycle.Observer; import com.android.internal.annotations.GuardedBy; import com.android.settingslib.volume.MediaSessions; import com.android.settingslib.volume.MediaSessions.SessionId; import com.android.systemui.Dumpable; import com.android.systemui.Flags; import com.android.systemui.broadcast.BroadcastDispatcher; Loading Loading @@ -1402,12 +1401,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } protected final class MediaSessionsCallbacks implements MediaSessions.Callbacks { private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>(); private final HashMap<SessionId, Integer> mRemoteStreams = new HashMap<>(); private int mNextStream = DYNAMIC_STREAM_REMOTE_START_INDEX; @Override public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) { public void onRemoteUpdate( SessionId token, String name, MediaSessions.VolumeInfo volumeInfo) { addStream(token, "onRemoteUpdate"); int stream = 0; Loading @@ -1415,14 +1415,15 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa stream = mRemoteStreams.get(token); } Slog.d(TAG, "onRemoteUpdate: stream: " + stream + " volume: " + pi.getCurrentVolume()); "onRemoteUpdate: stream: " + stream + " volume: " + volumeInfo.getCurrentVolume()); boolean changed = mState.states.indexOfKey(stream) < 0; final StreamState ss = streamStateW(stream); ss.dynamic = true; ss.levelMin = 0; ss.levelMax = pi.getMaxVolume(); if (ss.level != pi.getCurrentVolume()) { ss.level = pi.getCurrentVolume(); ss.levelMax = volumeInfo.getMaxVolume(); if (ss.level != volumeInfo.getCurrentVolume()) { ss.level = volumeInfo.getCurrentVolume(); changed = true; } if (!Objects.equals(ss.remoteLabel, name)) { Loading @@ -1437,11 +1438,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } @Override public void onRemoteVolumeChanged(Token token, int flags) { addStream(token, "onRemoteVolumeChanged"); public void onRemoteVolumeChanged(SessionId sessionId, int flags) { addStream(sessionId, "onRemoteVolumeChanged"); int stream = 0; synchronized (mRemoteStreams) { stream = mRemoteStreams.get(token); stream = mRemoteStreams.get(sessionId); } final boolean showUI = shouldShowUI(flags); Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI); Loading @@ -1459,7 +1460,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } @Override public void onRemoteRemoved(Token token) { public void onRemoteRemoved(SessionId token) { int stream; synchronized (mRemoteStreams) { if (!mRemoteStreams.containsKey(token)) { Loading @@ -1480,7 +1481,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } public void setStreamVolume(int stream, int level) { final Token token = findToken(stream); final SessionId token = findToken(stream); if (token == null) { Log.w(TAG, "setStreamVolume: No token found for stream: " + stream); return; Loading @@ -1488,9 +1489,9 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mMediaSessions.setVolume(token, level); } private Token findToken(int stream) { private SessionId findToken(int stream) { synchronized (mRemoteStreams) { for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) { for (Map.Entry<SessionId, Integer> entry : mRemoteStreams.entrySet()) { if (entry.getValue().equals(stream)) { return entry.getKey(); } Loading @@ -1499,7 +1500,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa return null; } private void addStream(Token token, String triggeringMethod) { private void addStream(SessionId token, String triggeringMethod) { synchronized (mRemoteStreams) { if (!mRemoteStreams.containsKey(token)) { mRemoteStreams.put(token, mNextStream); Loading