diff options
author | RoboErik <epastern@google.com> | 2014-06-23 15:38:48 -0700 |
---|---|---|
committer | RoboErik <epastern@google.com> | 2014-06-25 14:36:51 -0700 |
commit | 19c9518f6a817d53d5234de0020313cab6950b2f (patch) | |
tree | a9235b0a2a4fd973ba22b2dbf0c56e1f72a894de | |
parent | 0b16d7d807b213047bb018d565de8190a7362451 (diff) | |
download | frameworks_base-19c9518f6a817d53d5234de0020313cab6950b2f.zip frameworks_base-19c9518f6a817d53d5234de0020313cab6950b2f.tar.gz frameworks_base-19c9518f6a817d53d5234de0020313cab6950b2f.tar.bz2 |
b/15729204 Pipe sessions through to VolumePanel
When remote volume is changed via volume buttons we need to notify
the system UI so it can show the slider. This also passes it the
controller to use so adjustments to the slider are sent back to
the correct session.
Change-Id: If5847bcd5db16c56e0e9904b88c94e5b28954c41
18 files changed, 453 insertions, 237 deletions
@@ -306,6 +306,7 @@ LOCAL_SRC_FILES += \ media/java/android/media/IRemoteControlDisplay.aidl \ media/java/android/media/IRemoteDisplayCallback.aidl \ media/java/android/media/IRemoteDisplayProvider.aidl \ + media/java/android/media/IRemoteVolumeController.aidl \ media/java/android/media/IRemoteVolumeObserver.aidl \ media/java/android/media/IRingtonePlayer.aidl \ media/java/android/media/IVolumeController.aidl \ diff --git a/api/current.txt b/api/current.txt index 5e48e37..d9d17df 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15751,6 +15751,7 @@ package android.media.session { public final class MediaController { method public void addCallback(android.media.session.MediaController.Callback); method public void addCallback(android.media.session.MediaController.Callback, android.os.Handler); + method public void adjustVolumeBy(int, int); method public boolean dispatchMediaButtonEvent(android.view.KeyEvent); method public static android.media.session.MediaController fromToken(android.media.session.MediaSessionToken); method public android.media.MediaMetadata getMetadata(); @@ -15760,6 +15761,7 @@ package android.media.session { method public android.media.session.MediaController.VolumeInfo getVolumeInfo(); method public void removeCallback(android.media.session.MediaController.Callback); method public void sendControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver); + method public void setVolumeTo(int, int); } public static abstract class MediaController.Callback { @@ -15767,6 +15769,7 @@ package android.media.session { method public void onMetadataChanged(android.media.MediaMetadata); method public void onPlaybackStateChanged(android.media.session.PlaybackState); method public void onSessionEvent(java.lang.String, android.os.Bundle); + method public void onVolumeInfoChanged(android.media.session.MediaController.VolumeInfo); } public final class MediaController.TransportControls { @@ -15809,8 +15812,8 @@ package android.media.session { method public void setPlaybackToRemote(android.media.VolumeProvider); field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 - field public static final int VOLUME_TYPE_LOCAL = 1; // 0x1 - field public static final int VOLUME_TYPE_REMOTE = 2; // 0x2 + field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1 + field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2 } public static abstract class MediaSession.Callback { diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index fb19242..c8b51e0 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2811,38 +2811,6 @@ public class AudioManager { * Only useful for volume controllers. * @hide */ - public int getRemoteStreamVolume() { - // TODO STOPSHIP switch callers to use media sessions instead - Log.e(TAG, "Need to implement new Remote Volume!"); - return 0; - } - - /** - * Only useful for volume controllers. - * @hide - */ - public int getRemoteStreamMaxVolume() { - // TODO STOPSHIP switch callers to use media sessions instead - Log.e(TAG, "Need to implement new Remote Volume!"); - return 0; - } - - /** - * Only useful for volume controllers. - * @hide - */ - public void setRemoteStreamVolume(int index) { - try { - getService().setRemoteStreamVolume(index); - } catch (RemoteException e) { - Log.w(TAG, "Error setting remote stream volume", e); - } - } - - /** - * Only useful for volume controllers. - * @hide - */ public boolean isStreamAffectedByRingerMode(int streamType) { try { return getService().isStreamAffectedByRingerMode(streamType); diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 0c224a6..1588fe9 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -849,10 +849,8 @@ public class AudioService extends IAudioService.Stub { } if (streamType == STREAM_REMOTE_MUSIC) { - // don't play sounds for remote - flags &= ~(AudioManager.FLAG_PLAY_SOUND|AudioManager.FLAG_FIXED_VOLUME); - //if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()"); - mMediaFocusControl.adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags); + // TODO bounce it to MediaSessionService to find an appropriate + // session } else { adjustStreamVolume(streamType, direction, flags, callingPackage); } diff --git a/media/java/android/media/IRemoteVolumeController.aidl b/media/java/android/media/IRemoteVolumeController.aidl new file mode 100644 index 0000000..e4a4a42 --- /dev/null +++ b/media/java/android/media/IRemoteVolumeController.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 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 android.media; + +import android.media.session.ISessionController; + +/** + * AIDL for the MediaSessionService to report interesting events on remote playback + * to a volume control dialog. See also IVolumeController for the AudioService half. + * TODO add in better support for multiple remote sessions. + * @hide + */ +oneway interface IRemoteVolumeController { + void remoteVolumeChanged(ISessionController session, int flags); + // sets the default session to use with the slider, replaces remoteSliderVisibility + // on IVolumeController + void updateRemoteController(ISessionController session); +} diff --git a/media/java/android/media/IVolumeController.aidl b/media/java/android/media/IVolumeController.aidl index 35d7708..e3593a6 100644 --- a/media/java/android/media/IVolumeController.aidl +++ b/media/java/android/media/IVolumeController.aidl @@ -16,17 +16,12 @@ package android.media; - /** - * AIDL for the AudioService to report interesting events to a remote volume control dialog. + * AIDL for the AudioService to report interesting events to a volume control + * dialog in another process. * @hide */ oneway interface IVolumeController { - void hasNewRemotePlaybackInfo(); - - void remoteVolumeChanged(int streamType, int flags); - - void remoteSliderVisibility(boolean visible); void displaySafeVolumeWarning(int flags); diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java index a4a7c4e..1587b11 100644 --- a/media/java/android/media/MediaFocusControl.java +++ b/media/java/android/media/MediaFocusControl.java @@ -2061,29 +2061,6 @@ public class MediaFocusControl implements OnFinished { } } - protected void adjustRemoteVolume(int streamType, int direction, int flags) { - int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED; - boolean volFixed = false; - synchronized (mMainRemote) { - if (!mMainRemoteIsActive) { - if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client"); - return; - } - rccId = mMainRemote.mRccId; - volFixed = (mMainRemote.mVolumeHandling == - RemoteControlClient.PLAYBACK_VOLUME_FIXED); - } - // unlike "local" stream volumes, we can't compute the new volume based on the direction, - // we can only notify the remote that volume needs to be updated, and we'll get an async' - // update through setPlaybackInfoForRcc() - if (!volFixed) { - sendVolumeUpdateToRemote(rccId, direction); - } - - // fire up the UI - mVolumeController.postRemoteVolumeChanged(streamType, flags); - } - private void sendVolumeUpdateToRemote(int rccId, int direction) { if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); } if (direction == 0) { @@ -2183,27 +2160,9 @@ public class MediaFocusControl implements OnFinished { } private void onReevaluateRemote() { - if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); } - // is there a registered RemoteControlClient that is handling remote playback - boolean hasRemotePlayback = false; - synchronized (mPRStack) { - // iteration stops when PLAYBACK_TYPE_REMOTE is found, so remote control stack - // traversal order doesn't matter - Iterator<PlayerRecord> stackIterator = mPRStack.iterator(); - while(stackIterator.hasNext()) { - PlayerRecord prse = stackIterator.next(); - if (prse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) { - hasRemotePlayback = true; - break; - } - } - } - synchronized (mMainRemote) { - if (mHasRemotePlayback != hasRemotePlayback) { - mHasRemotePlayback = hasRemotePlayback; - mVolumeController.postRemoteSliderVisibility(hasRemotePlayback); - } - } + // TODO This was used to notify VolumePanel if there was remote playback + // in the stack. This is now in MediaSessionService. More code should be + // removed. } } diff --git a/media/java/android/media/VolumeController.java b/media/java/android/media/VolumeController.java index 6b70cc3..d1c51c5 100644 --- a/media/java/android/media/VolumeController.java +++ b/media/java/android/media/VolumeController.java @@ -23,7 +23,9 @@ import android.util.Log; import java.util.Objects; /** - * Wraps the remote volume controller interface as a convenience to audio service. + * Wraps the volume controller binder interface as a convenience to audio + * service. + * * @hide */ public class VolumeController { @@ -52,33 +54,6 @@ public class VolumeController { return "VolumeController(" + asBinder() + ")"; } - public void postHasNewRemotePlaybackInfo() { - if (mController == null) return; - try { - mController.hasNewRemotePlaybackInfo(); - } catch (RemoteException e) { - Log.w(TAG, "Error calling hasNewRemotePlaybackInfo", e); - } - } - - public void postRemoteVolumeChanged(int streamType, int flags) { - if (mController == null) return; - try { - mController.remoteVolumeChanged(streamType, flags); - } catch (RemoteException e) { - Log.w(TAG, "Error calling remoteVolumeChanged", e); - } - } - - public void postRemoteSliderVisibility(boolean visible) { - if (mController == null) return; - try { - mController.remoteSliderVisibility(visible); - } catch (RemoteException e) { - Log.w(TAG, "Error calling remoteSliderVisibility", e); - } - } - public void postDisplaySafeVolumeWarning(int flags) { if (mController == null) return; try { diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl index e823153..baa1379 100644 --- a/media/java/android/media/session/ISessionControllerCallback.aidl +++ b/media/java/android/media/session/ISessionControllerCallback.aidl @@ -17,6 +17,7 @@ package android.media.session; import android.media.MediaMetadata; import android.media.session.RouteInfo; +import android.media.session.ParcelableVolumeInfo; import android.media.session.PlaybackState; import android.os.Bundle; @@ -30,4 +31,5 @@ oneway interface ISessionControllerCallback { // These callbacks are for the TransportController void onPlaybackStateChanged(in PlaybackState state); void onMetadataChanged(in MediaMetadata metadata); + void onVolumeInfoChanged(in ParcelableVolumeInfo info); }
\ No newline at end of file diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index bd1fa85..dce84d4 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -16,6 +16,7 @@ package android.media.session; import android.content.ComponentName; +import android.media.IRemoteVolumeController; import android.media.session.IActiveSessionsListener; import android.media.session.ISession; import android.media.session.ISessionCallback; @@ -34,4 +35,7 @@ interface ISessionManager { void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName, int userId); void removeSessionsListener(in IActiveSessionsListener listener); + + // This is for the system volume UI only + void setRemoteVolumeController(in IRemoteVolumeController rvc); }
\ No newline at end of file diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index cc3db26..7653e5a 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -18,6 +18,7 @@ package android.media.session; import android.annotation.NonNull; import android.annotation.Nullable; +import android.media.AudioManager; import android.media.MediaMetadata; import android.media.Rating; import android.media.VolumeProvider; @@ -52,6 +53,7 @@ public final class MediaController { private static final int MSG_UPDATE_PLAYBACK_STATE = 2; private static final int MSG_UPDATE_METADATA = 3; private static final int MSG_ROUTE = 4; + private static final int MSG_UPDATE_VOLUME = 5; private final ISessionController mSessionBinder; @@ -203,6 +205,43 @@ public final class MediaController { } /** + * Set the volume of the stream or output this session is playing on. The + * command will be ignored if it does not support + * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in + * {@link AudioManager} may be used to affect the handling. + * + * @see #getVolumeInfo() + * @param value The value to set it to, between 0 and the reported max. + * @param flags Any flags to pass with the command. + */ + public void setVolumeTo(int value, int flags) { + try { + mSessionBinder.setVolumeTo(value, flags); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling setVolumeTo.", e); + } + } + + /** + * Adjust the volume of the stream or output this session is playing on. + * Negative values will lower the volume. The command will be ignored if it + * does not support {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or + * {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in + * {@link AudioManager} may be used to affect the handling. + * + * @see #getVolumeInfo() + * @param delta The number of steps to adjust the volume by. + * @param flags Any flags to pass with the command. + */ + public void adjustVolumeBy(int delta, int flags) { + try { + mSessionBinder.adjustVolumeBy(delta, flags); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling adjustVolumeBy.", e); + } + } + + /** * Adds a callback to receive updates from the Session. Updates will be * posted on the caller's thread. * @@ -406,6 +445,14 @@ public final class MediaController { */ public void onMetadataChanged(@Nullable MediaMetadata metadata) { } + + /** + * Override to handle changes to the volume info. + * + * @param info The current volume info for this session. + */ + public void onVolumeInfoChanged(VolumeInfo info) { + } } /** @@ -552,8 +599,8 @@ public final class MediaController { /** * Get the type of volume handling, either local or remote. One of: * <ul> - * <li>{@link MediaSession#VOLUME_TYPE_LOCAL}</li> - * <li>{@link MediaSession#VOLUME_TYPE_REMOTE}</li> + * <li>{@link MediaSession#PLAYBACK_TYPE_LOCAL}</li> + * <li>{@link MediaSession#PLAYBACK_TYPE_REMOTE}</li> * </ul> * * @return The type of volume handling this session is using. @@ -564,7 +611,7 @@ public final class MediaController { /** * Get the stream this is currently controlling volume on. When the volume - * type is {@link MediaSession#VOLUME_TYPE_REMOTE} this value does not + * type is {@link MediaSession#PLAYBACK_TYPE_REMOTE} this value does not * have meaning and should be ignored. * * @return The stream this session is playing on. @@ -646,6 +693,16 @@ public final class MediaController { } } + @Override + public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) { + MediaController controller = mController.get(); + if (controller != null) { + VolumeInfo info = new VolumeInfo(pvi.volumeType, pvi.audioStream, pvi.controlType, + pvi.maxVolume, pvi.currentVolume); + controller.postMessage(MSG_UPDATE_VOLUME, info, null); + } + } + } private final static class MessageHandler extends Handler { @@ -671,6 +728,9 @@ public final class MediaController { case MSG_UPDATE_METADATA: mCallback.onMetadataChanged((MediaMetadata) msg.obj); break; + case MSG_UPDATE_VOLUME: + mCallback.onVolumeInfoChanged((VolumeInfo) msg.obj); + break; } } diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 8909d55..7637ec8 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -127,12 +127,12 @@ public final class MediaSession { /** * The session uses local playback. */ - public static final int VOLUME_TYPE_LOCAL = 1; + public static final int PLAYBACK_TYPE_LOCAL = 1; /** * The session uses remote playback. */ - public static final int VOLUME_TYPE_REMOTE = 2; + public static final int PLAYBACK_TYPE_REMOTE = 2; private final Object mLock = new Object(); @@ -265,7 +265,7 @@ public final class MediaSession { */ public void setPlaybackToLocal(int stream) { try { - mBinder.configureVolumeHandling(VOLUME_TYPE_LOCAL, stream, 0); + mBinder.configureVolumeHandling(PLAYBACK_TYPE_LOCAL, stream, 0); } catch (RemoteException e) { Log.wtf(TAG, "Failure in setPlaybackToLocal.", e); } @@ -294,7 +294,7 @@ public final class MediaSession { }); try { - mBinder.configureVolumeHandling(VOLUME_TYPE_REMOTE, volumeProvider.getVolumeControl(), + mBinder.configureVolumeHandling(PLAYBACK_TYPE_REMOTE, volumeProvider.getVolumeControl(), volumeProvider.getMaxVolume()); } catch (RemoteException e) { Log.wtf(TAG, "Failure in setPlaybackToRemote.", e); diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 882453f..9291bb0 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; +import android.media.IRemoteVolumeController; import android.media.session.ISessionManager; import android.os.IBinder; import android.os.RemoteException; @@ -215,6 +216,21 @@ public final class MediaSessionManager { } /** + * Set the remote volume controller to receive volume updates on. Only for + * use by system UI. + * + * @param rvc The volume controller to receive updates on. + * @hide + */ + public void setRemoteVolumeController(IRemoteVolumeController rvc) { + try { + mService.setRemoteVolumeController(rvc); + } catch (RemoteException e) { + Log.e(TAG, "Error in setRemoteVolumeController.", e); + } + } + + /** * Send a media key event. The receiver will be selected automatically. * * @param keyEvent The KeyEvent to send. diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java index 1cab7ea..d514c99 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java @@ -34,6 +34,9 @@ import android.media.AudioService; import android.media.AudioSystem; import android.media.RingtoneManager; import android.media.ToneGenerator; +import android.media.VolumeProvider; +import android.media.session.MediaController; +import android.media.session.MediaController.VolumeInfo; import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; @@ -224,6 +227,7 @@ public class VolumePanel extends Handler { /** Object that contains data for each slider */ private class StreamControl { int streamType; + MediaController controller; ViewGroup group; ImageView icon; SeekBar seekbarView; @@ -405,7 +409,8 @@ public class VolumePanel extends Handler { if (streamType == STREAM_MASTER) { return mAudioManager.isMasterMute(); } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) { - return (mAudioManager.getRemoteStreamVolume() <= 0); + // TODO do we need to support a distinct mute property for remote? + return false; } else { return mAudioManager.isStreamMute(streamType); } @@ -415,7 +420,14 @@ public class VolumePanel extends Handler { if (streamType == STREAM_MASTER) { return mAudioManager.getMasterMaxVolume(); } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) { - return mAudioManager.getRemoteStreamMaxVolume(); + if (mStreamControls != null) { + StreamControl sc = mStreamControls.get(streamType); + if (sc != null && sc.controller != null) { + VolumeInfo vi = sc.controller.getVolumeInfo(); + return vi.getMaxVolume(); + } + } + return -1; } else { return mAudioManager.getStreamMaxVolume(streamType); } @@ -425,19 +437,32 @@ public class VolumePanel extends Handler { if (streamType == STREAM_MASTER) { return mAudioManager.getMasterVolume(); } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) { - return mAudioManager.getRemoteStreamVolume(); + if (mStreamControls != null) { + StreamControl sc = mStreamControls.get(streamType); + if (sc != null && sc.controller != null) { + VolumeInfo vi = sc.controller.getVolumeInfo(); + return vi.getCurrentVolume(); + } + } + return -1; } else { return mAudioManager.getStreamVolume(streamType); } } - private void setStreamVolume(int streamType, int index, int flags) { - if (streamType == STREAM_MASTER) { - mAudioManager.setMasterVolume(index, flags); - } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) { - mAudioManager.setRemoteStreamVolume(index); - } else { - mAudioManager.setStreamVolume(streamType, index, flags); + private void setStreamVolume(StreamControl sc, int index, int flags) { + if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) { + if (sc.controller != null) { + sc.controller.setVolumeTo(index, flags); + } else { + Log.wtf(mTag, "Adjusting remote volume without a controller!"); + } + } else if (getStreamVolume(sc.streamType) != index) { + if (sc.streamType == STREAM_MASTER) { + mAudioManager.setMasterVolume(index, flags); + } else { + mAudioManager.setStreamVolume(sc.streamType, index, flags); + } } } @@ -549,7 +574,7 @@ public class VolumePanel extends Handler { if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) { // never disable touch interactions for remote playback, the muting is not tied to // the state of the phone. - sc.seekbarView.setEnabled(true); + sc.seekbarView.setEnabled(!fixedVolume); } else if (fixedVolume || (sc.streamType != mAudioManager.getMasterStreamType() && muted) || (sConfirmSafeVolumeDialog != null)) { @@ -677,7 +702,7 @@ public class VolumePanel extends Handler { obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget(); } - public void postRemoteVolumeChanged(int streamType, int flags) { + public void postRemoteVolumeChanged(MediaController controller, int flags) { if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return; synchronized (this) { if (mStreamControls == null) { @@ -685,7 +710,7 @@ public class VolumePanel extends Handler { } } removeMessages(MSG_FREE_RESOURCES); - obtainMessage(MSG_REMOTE_VOLUME_CHANGED, streamType, flags).sendToTarget(); + obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget(); } public void postRemoteSliderVisibility(boolean visible) { @@ -758,7 +783,7 @@ public class VolumePanel extends Handler { if (mActiveStreamType != streamType) { reorderSliders(streamType); } - onShowVolumeChanged(streamType, flags); + onShowVolumeChanged(streamType, flags, null); } } @@ -790,7 +815,7 @@ public class VolumePanel extends Handler { onVolumeChanged(streamType, flags); } - protected void onShowVolumeChanged(int streamType, int flags) { + protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) { int index = getStreamVolume(streamType); mRingIsSilent = false; @@ -803,6 +828,7 @@ public class VolumePanel extends Handler { // get max volume for progress bar int max = getStreamMaxVolume(streamType); + StreamControl sc = mStreamControls.get(streamType); switch (streamType) { @@ -865,13 +891,37 @@ public class VolumePanel extends Handler { } case AudioService.STREAM_REMOTE_MUSIC: { + if (controller == null && sc != null) { + // If we weren't passed one try using the last one set. + controller = sc.controller; + } + if (controller == null) { + // We still don't have one, ignore the command. + Log.w(mTag, "sent remote volume change without a controller!"); + } else { + VolumeInfo vi = controller.getVolumeInfo(); + index = vi.getCurrentVolume(); + max = vi.getMaxVolume(); + if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) { + // if the remote volume is fixed add the flag for the UI + flags |= AudioManager.FLAG_FIXED_VOLUME; + } + } if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); } break; } } - StreamControl sc = mStreamControls.get(streamType); if (sc != null) { + if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) { + if (sc.controller != null) { + sc.controller.removeCallback(mMediaControllerCb); + } + sc.controller = controller; + if (controller != null) { + sc.controller.addCallback(mMediaControllerCb); + } + } if (sc.seekbarView.getMax() != max) { sc.seekbarView.setMax(max); } @@ -949,34 +999,21 @@ public class VolumePanel extends Handler { mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM); } - protected void onRemoteVolumeChanged(int streamType, int flags) { - // streamType is the real stream type being affected, but for the UI sliders, we - // refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real - // stream type. - if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")"); + protected void onRemoteVolumeChanged(MediaController controller, int flags) { + if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags + + ")"); if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) { synchronized (this) { if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) { reorderSliders(AudioService.STREAM_REMOTE_MUSIC); } - onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags); + onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller); } } else { if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI"); } - if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) { - removeMessages(MSG_PLAY_SOUND); - sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY); - } - - if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) { - removeMessages(MSG_PLAY_SOUND); - removeMessages(MSG_VIBRATE); - onStopSounds(); - } - removeMessages(MSG_FREE_RESOURCES); sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY); resetTimeout(); @@ -987,10 +1024,24 @@ public class VolumePanel extends Handler { if (isShowing() && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC) && (mStreamControls != null)) { - onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0); + onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null); } } + /** + * Clear the current remote stream controller. + */ + private void clearRemoteStreamController() { + if (mStreamControls != null) { + StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC); + if (sc != null) { + if (sc.controller != null) { + sc.controller.removeCallback(mMediaControllerCb); + sc.controller = null; + } + } + } + } /** * Handler for MSG_SLIDER_VISIBILITY_CHANGED @@ -1137,6 +1188,7 @@ public class VolumePanel extends Handler { if (isShowing()) { if (mDialog != null) { mDialog.dismiss(); + clearRemoteStreamController(); mActiveStreamType = -1; } } @@ -1155,7 +1207,7 @@ public class VolumePanel extends Handler { } case MSG_REMOTE_VOLUME_CHANGED: { - onRemoteVolumeChanged(msg.arg1, msg.arg2); + onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1); break; } @@ -1202,9 +1254,7 @@ public class VolumePanel extends Handler { final Object tag = seekBar.getTag(); if (fromUser && tag instanceof StreamControl) { StreamControl sc = (StreamControl) tag; - if (getStreamVolume(sc.streamType) != progress) { - setStreamVolume(sc.streamType, progress, 0); - } + setStreamVolume(sc, progress, 0); } resetTimeout(); } @@ -1215,19 +1265,6 @@ public class VolumePanel extends Handler { @Override public void onStopTrackingTouch(SeekBar seekBar) { - final Object tag = seekBar.getTag(); - if (tag instanceof StreamControl) { - StreamControl sc = (StreamControl) tag; - // Because remote volume updates are asynchronous, AudioService - // might have received a new remote volume value since the - // finger adjusted the slider. So when the progress of the - // slider isn't being tracked anymore, adjust the slider to the - // last "published" remote volume value, so the UI reflects the - // actual volume. - if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) { - seekBar.setProgress(getStreamVolume(AudioService.STREAM_REMOTE_MUSIC)); - } - } } }; @@ -1257,4 +1294,10 @@ public class VolumePanel extends Handler { postZenModeChanged(zen); } }; + + private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() { + public void onVolumeInfoChanged(VolumeInfo info) { + onRemoteVolumeUpdateIfShown(); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java index 7da90d8..c1f92ff 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java @@ -5,7 +5,11 @@ import android.content.Intent; import android.content.res.Resources; import android.database.ContentObserver; import android.media.AudioManager; +import android.media.IRemoteVolumeController; import android.media.IVolumeController; +import android.media.session.ISessionController; +import android.media.session.MediaController; +import android.media.session.MediaSessionManager; import android.net.Uri; import android.os.Handler; import android.os.RemoteException; @@ -42,12 +46,21 @@ public class VolumeUI extends SystemUI { private final Handler mHandler = new Handler(); private AudioManager mAudioManager; + private MediaSessionManager mMediaSessionManager; private VolumeController mVolumeController; + private RemoteVolumeController mRemoteVolumeController; + + private VolumePanel mDialogPanel; + private VolumePanel mPanel; @Override public void start() { mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - mVolumeController = new VolumeController(mContext); + mMediaSessionManager = (MediaSessionManager) mContext + .getSystemService(Context.MEDIA_SESSION_SERVICE); + initPanel(); + mVolumeController = new VolumeController(); + mRemoteVolumeController = new RemoteVolumeController(); putComponent(VolumeComponent.class, mVolumeController); updateController(); mContext.getContentResolver().registerContentObserver(SETTING_URI, false, mObserver); @@ -57,12 +70,32 @@ public class VolumeUI extends SystemUI { if (Settings.Global.getInt(mContext.getContentResolver(), SETTING, DEFAULT) != 0) { Log.d(TAG, "Registering volume controller"); mAudioManager.setVolumeController(mVolumeController); + mMediaSessionManager.setRemoteVolumeController(mRemoteVolumeController); } else { Log.d(TAG, "Unregistering volume controller"); mAudioManager.setVolumeController(null); + mMediaSessionManager.setRemoteVolumeController(null); } } + private void initPanel() { + mPanel = new VolumePanel(mContext, null, new ZenModeControllerImpl(mContext, mHandler)); + final int delay = mContext.getResources().getInteger(R.integer.feedback_start_delay); + mPanel.setZenModePanelCallback(new ZenModePanel.Callback() { + @Override + public void onMoreSettings() { + mHandler.removeCallbacks(mStartZenSettings); + mHandler.postDelayed(mStartZenSettings, delay); + } + + @Override + public void onInteraction() { + mDialogPanel.resetTimeout(); + } + }); + mDialogPanel = mPanel; + } + private final ContentObserver mObserver = new ContentObserver(mHandler) { public void onChange(boolean selfChange, Uri uri) { if (SETTING_URI.equals(uri)) { @@ -71,55 +104,18 @@ public class VolumeUI extends SystemUI { } }; - /** For now, simply host an unmodified base volume panel in this process. */ - private final class VolumeController extends IVolumeController.Stub implements VolumeComponent { - private final VolumePanel mDialogPanel; - private VolumePanel mPanel; - - public VolumeController(Context context) { - mPanel = new VolumePanel(context, null, new ZenModeControllerImpl(mContext, mHandler)); - final int delay = context.getResources().getInteger(R.integer.feedback_start_delay); - mPanel.setZenModePanelCallback(new ZenModePanel.Callback() { - @Override - public void onMoreSettings() { - mHandler.removeCallbacks(mStartZenSettings); - mHandler.postDelayed(mStartZenSettings, delay); - } - - @Override - public void onInteraction() { - mDialogPanel.resetTimeout(); - } - }); - mDialogPanel = mPanel; - } - - private final Runnable mStartZenSettings = new Runnable() { - @Override - public void run() { - mDialogPanel.postDismiss(); - final Intent intent = ZenModePanel.ZEN_SETTINGS; - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); - } - }; - - @Override - public void hasNewRemotePlaybackInfo() throws RemoteException { - mPanel.postHasNewRemotePlaybackInfo(); - } - + private final Runnable mStartZenSettings = new Runnable() { @Override - public void remoteVolumeChanged(int streamType, int flags) - throws RemoteException { - mPanel.postRemoteVolumeChanged(streamType, flags); + public void run() { + mDialogPanel.postDismiss(); + final Intent intent = ZenModePanel.ZEN_SETTINGS; + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); } + }; - @Override - public void remoteSliderVisibility(boolean visible) - throws RemoteException { - mPanel.postRemoteSliderVisibility(visible); - } + /** For now, simply host an unmodified base volume panel in this process. */ + private final class VolumeController extends IVolumeController.Stub implements VolumeComponent { @Override public void displaySafeVolumeWarning(int flags) throws RemoteException { @@ -163,4 +159,21 @@ public class VolumeUI extends SystemUI { mPanel = panel == null ? mDialogPanel : panel; } } + + private final class RemoteVolumeController extends IRemoteVolumeController.Stub { + + @Override + public void remoteVolumeChanged(ISessionController binder, int flags) + throws RemoteException { + MediaController controller = MediaController.fromBinder(binder); + mPanel.postRemoteVolumeChanged(controller, flags); + } + + @Override + public void updateRemoteController(ISessionController session) throws RemoteException { + mPanel.postRemoteSliderVisibility(session != null); + // TODO stash default session in case the slider can be opened other + // than by remoteVolumeChanged. + } + } } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 1264741..6f1eb8f 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -16,11 +16,9 @@ package com.android.server.media; -import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.media.routeprovider.RouteRequest; import android.media.session.ISessionController; import android.media.session.ISessionControllerCallback; @@ -49,7 +47,6 @@ import android.os.Message; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; -import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -87,6 +84,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { */ private static final int ACTIVE_BUFFER = 30000; + /** + * The amount of time we'll send an assumed volume after the last volume + * command before reverting to the last reported volume. + */ + private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000; + private final MessageHandler mHandler; private final int mOwnerPid; @@ -122,11 +125,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { // Volume handling fields private AudioManager mAudioManager; - private int mVolumeType = MediaSession.VOLUME_TYPE_LOCAL; + private int mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL; private int mAudioStream = AudioManager.STREAM_MUSIC; private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE; private int mMaxVolume = 0; private int mCurrentVolume = 0; + private int mOptimisticVolume = -1; // End volume handling fields private boolean mIsActive = false; @@ -276,7 +280,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { * @param delta The amount to adjust the volume by. */ public void adjustVolumeBy(int delta, int flags) { - if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) { + if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) { if (delta == 0) { mAudioManager.adjustStreamVolume(mAudioStream, delta, flags); } else { @@ -298,18 +302,46 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { return; } mSessionCb.adjustVolumeBy(delta); + + int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); + mOptimisticVolume = volumeBefore + delta; + mOptimisticVolume = Math.max(0, Math.min(mOptimisticVolume, mMaxVolume)); + mHandler.removeCallbacks(mClearOptimisticVolumeRunnable); + mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT); + if (volumeBefore != mOptimisticVolume) { + pushVolumeUpdate(); + } + + if (DEBUG) { + Log.d(TAG, "Adjusted optimistic volume to " + mOptimisticVolume + " max is " + + mMaxVolume); + } } } public void setVolumeTo(int value, int flags) { - if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) { + if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) { mAudioManager.setStreamVolume(mAudioStream, value, flags); } else { if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) { // Nothing to do. The volume can't be set directly. return; } + value = Math.max(0, Math.min(value, mMaxVolume)); mSessionCb.setVolumeTo(value); + + int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume); + mOptimisticVolume = Math.max(0, Math.min(value, mMaxVolume)); + mHandler.removeCallbacks(mClearOptimisticVolumeRunnable); + mHandler.postDelayed(mClearOptimisticVolumeRunnable, OPTIMISTIC_VOLUME_TIMEOUT); + if (volumeBefore != mOptimisticVolume) { + pushVolumeUpdate(); + } + + if (DEBUG) { + Log.d(TAG, "Set optimistic volume to " + mOptimisticVolume + " max is " + + mMaxVolume); + } } } @@ -427,6 +459,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } /** + * Get the volume we'd like it to be set to. This is only valid for a short + * while after a call to adjust or set volume. + * + * @return The current optimistic volume or -1. + */ + public int getOptimisticVolume() { + return mOptimisticVolume; + } + + /** * @return True if this session is currently connected to a route. */ public boolean isConnected() { @@ -542,8 +584,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { cb.onPlaybackStateChanged(mPlaybackState); } catch (DeadObjectException e) { mControllerCallbacks.remove(i); - Log.w(TAG, "Removed dead callback in pushPlaybackStateUpdate. size=" - + mControllerCallbacks.size() + " cb=" + cb, e); + Log.w(TAG, "Removed dead callback in pushPlaybackStateUpdate.", e); } catch (RemoteException e) { Log.w(TAG, "unexpected exception in pushPlaybackStateUpdate.", e); } @@ -561,10 +602,29 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { try { cb.onMetadataChanged(mMetadata); } catch (DeadObjectException e) { - Log.w(TAG, "Removing dead callback in pushMetadataUpdate. " + cb, e); + Log.w(TAG, "Removing dead callback in pushMetadataUpdate. ", e); mControllerCallbacks.remove(i); } catch (RemoteException e) { - Log.w(TAG, "unexpected exception in pushMetadataUpdate. " + cb, e); + Log.w(TAG, "unexpected exception in pushMetadataUpdate. ", e); + } + } + } + } + + private void pushVolumeUpdate() { + synchronized (mLock) { + if (mDestroyed) { + return; + } + ParcelableVolumeInfo info = mController.getVolumeAttributes(); + for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { + ISessionControllerCallback cb = mControllerCallbacks.get(i); + try { + cb.onVolumeInfoChanged(info); + } catch (DeadObjectException e) { + Log.w(TAG, "Removing dead callback in pushVolumeUpdate. ", e); + } catch (RemoteException e) { + Log.w(TAG, "Unexpected exception in pushVolumeUpdate. ", e); } } } @@ -680,6 +740,17 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } }; + private final Runnable mClearOptimisticVolumeRunnable = new Runnable() { + @Override + public void run() { + boolean needUpdate = (mOptimisticVolume != mCurrentVolume); + mOptimisticVolume = -1; + if (needUpdate) { + pushVolumeUpdate(); + } + } + }; + private final class SessionStub extends ISession.Stub { @Override public void destroy() { @@ -785,12 +856,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { @Override public void setCurrentVolume(int volume) { mCurrentVolume = volume; + mHandler.post(MessageHandler.MSG_UPDATE_VOLUME); } @Override public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException { + boolean typeChanged = type != mVolumeType; switch(type) { - case MediaSession.VOLUME_TYPE_LOCAL: + case MediaSession.PLAYBACK_TYPE_LOCAL: mVolumeType = type; int audioStream = arg1; if (isValidStream(audioStream)) { @@ -800,7 +873,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mAudioStream = AudioManager.STREAM_MUSIC; } break; - case MediaSession.VOLUME_TYPE_REMOTE: + case MediaSession.PLAYBACK_TYPE_REMOTE: mVolumeType = type; mVolumeControlType = arg1; mMaxVolume = arg2; @@ -809,6 +882,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { throw new IllegalArgumentException("Volume handling type " + type + " not recognized."); } + if (typeChanged) { + mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this); + } } private boolean isValidStream(int stream) { @@ -1027,10 +1103,11 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { int type; int max; int current; - if (mVolumeType == MediaSession.VOLUME_TYPE_REMOTE) { + if (mVolumeType == MediaSession.PLAYBACK_TYPE_REMOTE) { type = mVolumeControlType; max = mMaxVolume; - current = mCurrentVolume; + current = mOptimisticVolume != -1 ? mOptimisticVolume + : mCurrentVolume; } else { type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE; max = mAudioManager.getStreamMaxVolume(mAudioStream); @@ -1130,6 +1207,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private static final int MSG_UPDATE_ROUTE_FILTERS = 5; private static final int MSG_SEND_COMMAND = 6; private static final int MSG_UPDATE_SESSION_STATE = 7; + private static final int MSG_UPDATE_VOLUME = 8; public MessageHandler(Looper looper) { super(looper); @@ -1157,6 +1235,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { case MSG_UPDATE_SESSION_STATE: // TODO add session state break; + case MSG_UPDATE_VOLUME: + pushVolumeUpdate(); + break; } } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index c0b7d68..873ed71 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -27,8 +27,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.media.AudioManager; import android.media.IAudioService; +import android.media.IRemoteVolumeController; import android.media.routeprovider.RouteRequest; import android.media.session.IActiveSessionsListener; import android.media.session.ISession; @@ -98,6 +98,10 @@ public class MediaSessionService extends SystemService implements Monitor { // session so we drop late callbacks properly. private int mShowRoutesRequestId = 0; + // Used to notify system UI when remote volume was changed. TODO find a + // better way to handle this. + private IRemoteVolumeController mRvc; + // TODO refactor to have per user state for providers. See // MediaRouterService for an example @@ -225,6 +229,16 @@ public class MediaSessionService extends SystemService implements Monitor { } } + public void onSessionPlaybackTypeChanged(MediaSessionRecord record) { + synchronized (mLock) { + if (!mAllSessions.contains(record)) { + Log.d(TAG, "Unknown session changed playback type. Ignoring."); + return; + } + pushRemoteVolumeUpdateLocked(record.getUserId()); + } + } + @Override public void onStartUser(int userHandle) { updateUser(); @@ -367,6 +381,13 @@ public class MediaSessionService extends SystemService implements Monitor { } } + private void enforceStatusBarPermission(String action, int pid, int uid) { + if (getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE, + pid, uid) != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Only system ui may " + action); + } + } + /** * This checks if the component is an enabled notification listener for the * specified user. Enabled components may only operate on behalf of the user @@ -497,6 +518,7 @@ public class MediaSessionService extends SystemService implements Monitor { for (int i = 0; i < size; i++) { tokens.add(new MediaSessionToken(records.get(i).getControllerBinder())); } + pushRemoteVolumeUpdateLocked(userId); for (int i = mSessionsListeners.size() - 1; i >= 0; i--) { SessionsListenerRecord record = mSessionsListeners.get(i); if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) { @@ -512,6 +534,17 @@ public class MediaSessionService extends SystemService implements Monitor { } } + private void pushRemoteVolumeUpdateLocked(int userId) { + if (mRvc != null) { + try { + MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId); + mRvc.updateRemoteController(record == null ? null : record.getControllerBinder()); + } catch (RemoteException e) { + Log.wtf(TAG, "Error sending default remote volume to sys ui.", e); + } + } + } + private void persistMediaButtonReceiverLocked(MediaSessionRecord record) { ComponentName receiver = record.getMediaButtonReceiver(); if (receiver != null) { @@ -844,6 +877,19 @@ public class MediaSessionService extends SystemService implements Monitor { } @Override + public void setRemoteVolumeController(IRemoteVolumeController rvc) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + try { + enforceStatusBarPermission("listen for volume changes", pid, uid); + mRvc = rvc; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { @@ -929,6 +975,13 @@ public class MediaSessionService extends SystemService implements Monitor { } } else { session.adjustVolumeBy(delta, flags); + if (mRvc != null) { + try { + mRvc.remoteVolumeChanged(session.getControllerBinder(), flags); + } catch (Exception e) { + Log.wtf(TAG, "Error sending volume change to system UI.", e); + } + } } } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 144ccfa..e26a2eb 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -203,6 +203,19 @@ public class MediaSessionStack { return null; } + public MediaSessionRecord getDefaultRemoteSession(int userId) { + ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId); + + int size = records.size(); + for (int i = 0; i < size; i++) { + MediaSessionRecord record = records.get(i); + if (record.getPlaybackType() == MediaSession.PLAYBACK_TYPE_REMOTE) { + return record; + } + } + return null; + } + public void dump(PrintWriter pw, String prefix) { ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0, UserHandle.USER_ALL); |