summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorJean-Michel Trivi <jmtrivi@google.com>2012-06-20 09:10:18 -0700
committerAndroid Git Automerger <android-git-automerger@android.com>2012-06-20 09:10:18 -0700
commitb92d2a13c4ab34862968f47191d2713de3701757 (patch)
tree5e8bf4d95313db452e96c47b15df24a744bc82c5 /media
parenteb47f9c81dee7c277e77f77ff977ec5f1ded5bc1 (diff)
parent18ddb6ce6f3672a24a6f86ee4b28f5baa746bc20 (diff)
downloadframeworks_base-b92d2a13c4ab34862968f47191d2713de3701757.zip
frameworks_base-b92d2a13c4ab34862968f47191d2713de3701757.tar.gz
frameworks_base-b92d2a13c4ab34862968f47191d2713de3701757.tar.bz2
am 18ddb6ce: Merge "Remote volume handling in MediaRouter" into jb-dev
* commit '18ddb6ce6f3672a24a6f86ee4b28f5baa746bc20': Remote volume handling in MediaRouter
Diffstat (limited to 'media')
-rw-r--r--media/java/android/media/AudioService.java50
-rw-r--r--media/java/android/media/IAudioService.aidl2
-rw-r--r--media/java/android/media/IRemoteVolumeObserver.aidl26
-rw-r--r--media/java/android/media/MediaRouter.java269
-rw-r--r--media/java/android/media/RemoteControlClient.java29
5 files changed, 350 insertions, 26 deletions
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 418dc52..1b47602 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -4243,6 +4243,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
public int mPlaybackVolumeHandling;
public int mPlaybackStream;
public int mPlaybackState;
+ public IRemoteVolumeObserver mRemoteVolumeObs;
public void resetPlaybackInfo() {
mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
@@ -4251,6 +4252,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
mPlaybackStream = AudioManager.STREAM_MUSIC;
mPlaybackState = RemoteControlClient.PLAYSTATE_STOPPED;
+ mRemoteVolumeObs = null;
}
/** precondition: mediaIntent != null, eventReceiver != null */
@@ -4335,7 +4337,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
" -- state: " + rcse.mPlaybackState +
" -- vol handling: " + rcse.mPlaybackVolumeHandling +
" -- vol: " + rcse.mPlaybackVolume +
- " -- volMax: " + rcse.mPlaybackVolumeMax);
+ " -- volMax: " + rcse.mPlaybackVolumeMax +
+ " -- volObs: " + rcse.mRemoteVolumeObs);
+
}
}
synchronized (mMainRemote) {
@@ -5018,6 +5022,20 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
+ // FIXME send a message instead of updating the stack synchronously
+ public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
+ synchronized(mRCStack) {
+ Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+ while(stackIterator.hasNext()) {
+ RemoteControlStackEntry rcse = stackIterator.next();
+ if (rcse.mRccId == rccId) {
+ rcse.mRemoteVolumeObs = rvo;
+ break;
+ }
+ }
+ }
+ }
+
/**
* Checks if a remote client is active on the supplied stream type. Update the remote stream
* volume state if found and playing
@@ -5100,23 +5118,24 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// only handling discrete events
return;
}
- String packageForRcc = null;
+ IRemoteVolumeObserver rvo = null;
synchronized (mRCStack) {
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
while(stackIterator.hasNext()) {
RemoteControlStackEntry rcse = stackIterator.next();
//FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
if (rcse.mRccId == rccId) {
- packageForRcc = rcse.mReceiverComponent.getPackageName();
+ rvo = rcse.mRemoteVolumeObs;
break;
}
}
}
- if (packageForRcc != null) {
- Intent intent = new Intent(Intent.ACTION_VOLUME_UPDATE);
- intent.putExtra(Intent.EXTRA_VOLUME_UPDATE_DIRECTION, direction);
- intent.setPackage(packageForRcc);
- mContext.sendBroadcast(intent);
+ if (rvo != null) {
+ try {
+ rvo.dispatchRemoteVolumeUpdate(direction, -1);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error dispatching relative volume update", e);
+ }
}
}
@@ -5147,23 +5166,24 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
rccId = mMainRemote.mRccId;
}
- String packageForRcc = null;
+ IRemoteVolumeObserver rvo = null;
synchronized (mRCStack) {
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
while(stackIterator.hasNext()) {
RemoteControlStackEntry rcse = stackIterator.next();
if (rcse.mRccId == rccId) {
//FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
- packageForRcc = rcse.mReceiverComponent.getPackageName();
+ rvo = rcse.mRemoteVolumeObs;
break;
}
}
}
- if (packageForRcc != null) {
- Intent intent = new Intent(Intent.ACTION_VOLUME_UPDATE);
- intent.putExtra(Intent.EXTRA_VOLUME_UPDATE_VALUE, vol);
- intent.setPackage(packageForRcc);
- mContext.sendBroadcast(intent);
+ if (rvo != null) {
+ try {
+ rvo.dispatchRemoteVolumeUpdate(0, vol);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error dispatching absolute volume update", e);
+ }
}
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 83483c6..854eb3f 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -24,6 +24,7 @@ import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
import android.media.IRemoteControlClient;
import android.media.IRemoteControlDisplay;
+import android.media.IRemoteVolumeObserver;
import android.media.IRingtonePlayer;
import android.net.Uri;
import android.view.KeyEvent;
@@ -135,6 +136,7 @@ interface IAudioService {
oneway void setPlaybackInfoForRcc(int rccId, int what, int value);
int getRemoteStreamMaxVolume();
int getRemoteStreamVolume();
+ oneway void registerRemoteVolumeObserverForRcc(int rccId, in IRemoteVolumeObserver rvo);
void startBluetoothSco(IBinder cb);
void stopBluetoothSco(IBinder cb);
diff --git a/media/java/android/media/IRemoteVolumeObserver.aidl b/media/java/android/media/IRemoteVolumeObserver.aidl
new file mode 100644
index 0000000..7ff0c06
--- /dev/null
+++ b/media/java/android/media/IRemoteVolumeObserver.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 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;
+
+
+/**
+ * AIDL for the AudioService to report requests for remote volume update requests.
+ * @hide
+ */
+oneway interface IRemoteVolumeObserver {
+ void dispatchRemoteVolumeUpdate(int direction, int value);
+}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index a9e6e3d..a309c3f 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -589,9 +589,46 @@ public class MediaRouter {
RouteGroup mGroup;
final RouteCategory mCategory;
Drawable mIcon;
+ // playback information
+ int mPlaybackType = PLAYBACK_TYPE_LOCAL;
+ int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
+ int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
+ int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
+ int mPlaybackStream = AudioManager.STREAM_MUSIC;
+ VolumeCallbackInfo mVcb;
private Object mTag;
+ /**
+ * @hide (to be un-hidden)
+ * The default playback type, "local", indicating the presentation of the media is happening
+ * on the same device (e.g. a phone, a tablet) as where it is controlled from.
+ * @see #setPlaybackType(int)
+ */
+ public final static int PLAYBACK_TYPE_LOCAL = 0;
+ /**
+ * @hide (to be un-hidden)
+ * A playback type indicating the presentation of the media is happening on
+ * a different device (i.e. the remote device) than where it is controlled from.
+ * @see #setPlaybackType(int)
+ */
+ public final static int PLAYBACK_TYPE_REMOTE = 1;
+ /**
+ * @hide (to be un-hidden)
+ * Playback information indicating the playback volume is fixed, i.e. it cannot be
+ * controlled from this object. An example of fixed playback volume is a remote player,
+ * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
+ * than attenuate at the source.
+ * @see #setVolumeHandling(int)
+ */
+ public final static int PLAYBACK_VOLUME_FIXED = 0;
+ /**
+ * @hide (to be un-hidden)
+ * Playback information indicating the playback volume is variable and can be controlled
+ * from this object.
+ */
+ public final static int PLAYBACK_VOLUME_VARIABLE = 1;
+
RouteInfo(RouteCategory category) {
mCategory = category;
}
@@ -685,6 +722,71 @@ public class MediaRouter {
return mTag;
}
+ /**
+ * @hide (to be un-hidden)
+ * @return the type of playback associated with this route
+ * @see UserRouteInfo#setPlaybackType(int)
+ */
+ public int getPlaybackType() {
+ return mPlaybackType;
+ }
+
+ /**
+ * @hide (to be un-hidden)
+ * @return the stream over which the playback associated with this route is performed
+ * @see UserRouteInfo#setPlaybackStream(int)
+ */
+ public int getPlaybackStream() {
+ return mPlaybackStream;
+ }
+
+ /**
+ * @hide (to be un-hidden)
+ * @return the volume at which the playback associated with this route is performed
+ * @see UserRouteInfo#setVolume(int)
+ */
+ public int getVolume() {
+ if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
+ int vol = 0;
+ try {
+ vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting local stream volume", e);
+ }
+ return vol;
+ } else {
+ return mVolume;
+ }
+ }
+
+ /**
+ * @hide (to be un-hidden)
+ * @return the maximum volume at which the playback associated with this route is performed
+ * @see UserRouteInfo#setVolumeMax(int)
+ */
+ public int getVolumeMax() {
+ if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
+ int volMax = 0;
+ try {
+ volMax = sStatic.mAudioService.getStreamMaxVolume(mPlaybackStream);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error getting local stream volume", e);
+ }
+ return volMax;
+ } else {
+ return mVolumeMax;
+ }
+ }
+
+ /**
+ * @hide (to be un-hidden)
+ * @return how volume is handling on the route
+ * @see UserRouteInfo#setVolumeHandling(int)
+ */
+ public int getVolumeHandling() {
+ return mVolumeHandling;
+ }
+
void setStatusInt(CharSequence status) {
if (!status.equals(mStatus)) {
mStatus = status;
@@ -695,6 +797,24 @@ public class MediaRouter {
}
}
+ final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() {
+ public void dispatchRemoteVolumeUpdate(final int direction, final int value) {
+ sStatic.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ //Log.d(TAG, "dispatchRemoteVolumeUpdate dir=" + direction + " val=" + value);
+ if (mVcb != null) {
+ if (direction != 0) {
+ mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
+ } else {
+ mVcb.vcb.onVolumeSetRequest(mVcb.route, value);
+ }
+ }
+ }
+ });
+ }
+ };
+
void routeUpdated() {
updateRoute(this);
}
@@ -757,10 +877,14 @@ public class MediaRouter {
* RemoteControlClient will be used to reflect and update information
* such as route volume info in related UIs.</p>
*
+ * <p>The RemoteControlClient must have been previously registered with
+ * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.</p>
+ *
* @param rcc RemoteControlClient associated with this route
*/
public void setRemoteControlClient(RemoteControlClient rcc) {
mRcc = rcc;
+ updatePlaybackInfoOnRcc();
}
/**
@@ -792,6 +916,111 @@ public class MediaRouter {
public void setIconResource(int resId) {
setIconDrawable(sStatic.mResources.getDrawable(resId));
}
+
+ /**
+ * @hide (to be un-hidden)
+ * Set a callback to be notified of volume update requests
+ * @param vcb
+ */
+ public void setVolumeCallback(VolumeCallback vcb) {
+ mVcb = new VolumeCallbackInfo(vcb, this);
+ }
+
+ /**
+ * @hide (to be un-hidden)
+ * Defines whether playback associated with this route is "local"
+ * ({@link RouteInfo#PLAYBACK_TYPE_LOCAL}) or "remote"
+ * ({@link RouteInfo#PLAYBACK_TYPE_REMOTE}).
+ * @param type
+ */
+ public void setPlaybackType(int type) {
+ if (mPlaybackType != type) {
+ mPlaybackType = type;
+ setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type);
+ }
+ }
+
+ /**
+ * @hide (to be un-hidden)
+ * Defines whether volume for the playback associated with this route is fixed
+ * ({@link RouteInfo#PLAYBACK_VOLUME_FIXED}) or can modified
+ * ({@link RouteInfo#PLAYBACK_VOLUME_VARIABLE}).
+ * @param volumeHandling
+ */
+ public void setVolumeHandling(int volumeHandling) {
+ if (mVolumeHandling != volumeHandling) {
+ mVolumeHandling = volumeHandling;
+ setPlaybackInfoOnRcc(
+ RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling);
+ }
+ }
+
+ /**
+ * @hide (to be un-hidden)
+ * Defines at what volume the playback associated with this route is performed (for user
+ * feedback purposes). This information is only used when the playback is not local.
+ * @param volume
+ */
+ public void setVolume(int volume) {
+ if (mVolume != volume) {
+ mVolume = volume;
+ setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume);
+ }
+ }
+
+ /**
+ * @hide (to be un-hidden)
+ * Defines the maximum volume at which the playback associated with this route is performed
+ * (for user feedback purposes). This information is only used when the playback is not
+ * local.
+ * @param volumeMax
+ */
+ public void setVolumeMax(int volumeMax) {
+ if (mVolumeMax != volumeMax) {
+ mVolumeMax = volumeMax;
+ setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax);
+ }
+ }
+
+ /**
+ * @hide (to be un-hidden)
+ * Defines over what stream type the media is presented.
+ * @param stream
+ */
+ public void setPlaybackStream(int stream) {
+ if (mPlaybackStream != stream) {
+ mPlaybackStream = stream;
+ setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream);
+ }
+ }
+
+ private void updatePlaybackInfoOnRcc() {
+ if ((mRcc != null) && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) {
+ mRcc.setPlaybackInformation(
+ RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax);
+ mRcc.setPlaybackInformation(
+ RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume);
+ mRcc.setPlaybackInformation(
+ RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling);
+ mRcc.setPlaybackInformation(
+ RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream);
+ mRcc.setPlaybackInformation(
+ RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType);
+ // let AudioService know whom to call when remote volume needs to be updated
+ try {
+ sStatic.mAudioService.registerRemoteVolumeObserverForRcc(
+ mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error registering remote volume observer", e);
+ }
+ }
+ }
+
+ private void setPlaybackInfoOnRcc(int what, int value) {
+ if (mRcc != null) {
+ mRcc.setPlaybackInformation(what, value);
+ }
+ }
}
/**
@@ -1206,4 +1435,44 @@ public class MediaRouter {
}
}
+
+ static class VolumeCallbackInfo {
+ public final VolumeCallback vcb;
+ public final RouteInfo route;
+
+ public VolumeCallbackInfo(VolumeCallback vcb, RouteInfo route) {
+ this.vcb = vcb;
+ this.route = route;
+ }
+ }
+
+ /**
+ * @hide (to be un-hidden)
+ * Interface for receiving events about volume changes.
+ * All methods of this interface will be called from the application's main thread.
+ *
+ * <p>A VolumeCallback will only receive events relevant to routes that the callback
+ * was registered for.</p>
+ *
+ * @see UserRouteInfo#setVolumeCallback(VolumeCallback)
+ */
+ public static abstract class VolumeCallback {
+ /**
+ * Called when the volume for the route should be increased or decreased.
+ * @param info the route affected by this event
+ * @param direction an integer indicating whether the volume is to be increased
+ * (positive value) or decreased (negative value).
+ * For bundled changes, the absolute value indicates the number of changes
+ * in the same direction, e.g. +3 corresponds to three "volume up" changes.
+ */
+ public abstract void onVolumeUpdateRequest(RouteInfo info, int direction);
+ /**
+ * Called when the volume for the route should be set to the given value
+ * @param info the route affected by this event
+ * @param volume an integer indicating the new volume value that should be used, always
+ * between 0 and the value set by {@link UserRouteInfo#setVolumeMax(int)}.
+ */
+ public abstract void onVolumeSetRequest(RouteInfo info, int volume);
+ }
+
}
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 5b8035e..79f9d37 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -134,13 +134,13 @@ public class RemoteControlClient
public final static int PLAYSTATE_NONE = 0;
/**
- * @hide (to be un-hidden)
+ * @hide
* The default playback type, "local", indicating the presentation of the media is happening on
* the same device (e.g. a phone, a tablet) as where it is controlled from.
*/
public final static int PLAYBACK_TYPE_LOCAL = 0;
/**
- * @hide (to be un-hidden)
+ * @hide
* A playback type indicating the presentation of the media is happening on
* a different device (i.e. the remote device) than where it is controlled from.
*/
@@ -148,7 +148,7 @@ public class RemoteControlClient
private final static int PLAYBACK_TYPE_MIN = PLAYBACK_TYPE_LOCAL;
private final static int PLAYBACK_TYPE_MAX = PLAYBACK_TYPE_REMOTE;
/**
- * @hide (to be un-hidden)
+ * @hide
* Playback information indicating the playback volume is fixed, i.e. it cannot be controlled
* from this object. An example of fixed playback volume is a remote player, playing over HDMI
* where the user prefer to control the volume on the HDMI sink, rather than attenuate at the
@@ -157,7 +157,7 @@ public class RemoteControlClient
*/
public final static int PLAYBACK_VOLUME_FIXED = 0;
/**
- * @hide (to be un-hidden)
+ * @hide
* Playback information indicating the playback volume is variable and can be controlled from
* this object.
* @see #PLAYBACKINFO_VOLUME_HANDLING.
@@ -173,34 +173,34 @@ public class RemoteControlClient
//==========================================
// Public keys for playback information
/**
- * @hide (to be un-hidden)
+ * @hide
* Playback information that defines the type of playback associated with this
* RemoteControlClient. See {@link #PLAYBACK_TYPE_LOCAL} and {@link #PLAYBACK_TYPE_REMOTE}.
*/
public final static int PLAYBACKINFO_PLAYBACK_TYPE = 1;
/**
- * @hide (to be un-hidden)
+ * @hide
* Playback information that defines at what volume the playback associated with this
* RemoteControlClient is performed. This information is only used when the playback type is not
* local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}).
*/
public final static int PLAYBACKINFO_VOLUME = 2;
/**
- * @hide (to be un-hidden)
+ * @hide
* Playback information that defines the maximum volume volume value that is supported
* by the playback associated with this RemoteControlClient. This information is only used
* when the playback type is not local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}).
*/
public final static int PLAYBACKINFO_VOLUME_MAX = 3;
/**
- * @hide (to be un-hidden)
+ * @hide
* Playback information that defines how volume is handled for the presentation of the media.
* @see #PLAYBACK_VOLUME_FIXED
* @see #PLAYBACK_VOLUME_VARIABLE
*/
public final static int PLAYBACKINFO_VOLUME_HANDLING = 4;
/**
- * @hide (to be un-hidden)
+ * @hide
* Playback information that defines over what stream type the media is presented.
*/
public final static int PLAYBACKINFO_USES_STREAM = 5;
@@ -642,7 +642,7 @@ public class RemoteControlClient
private int mPlaybackStream = AudioManager.STREAM_MUSIC;
/**
- * @hide (to be un-hidden)
+ * @hide
* Set information describing information related to the playback of media so the system
* can implement additional behavior to handle non-local playback usecases.
* @param what a key to specify the type of information to set. Valid keys are
@@ -713,7 +713,7 @@ public class RemoteControlClient
}
/**
- * @hide (to be un-hidden)
+ * @hide
* Return playback information represented as an integer value.
* @param what a key to specify the type of information to retrieve. Valid keys are
* {@link #PLAYBACKINFO_PLAYBACK_TYPE},
@@ -899,6 +899,13 @@ public class RemoteControlClient
mRcseId = id;
}
+ /**
+ * @hide
+ */
+ public int getRcseId() {
+ return mRcseId;
+ }
+
private EventHandler mEventHandler;
private final static int MSG_REQUEST_PLAYBACK_STATE = 1;
private final static int MSG_REQUEST_METADATA = 2;