am 18ddb6ce: Merge "Remote volume handling in MediaRouter" into jb-dev

* commit '18ddb6ce6f3672a24a6f86ee4b28f5baa746bc20':
  Remote volume handling in MediaRouter
This commit is contained in:
Jean-Michel Trivi
2012-06-20 09:10:18 -07:00
committed by Android Git Automerger
7 changed files with 352 additions and 60 deletions

View File

@@ -193,11 +193,12 @@ LOCAL_SRC_FILES += \
location/java/android/location/INetInitiatedListener.aidl \
media/java/android/media/IAudioService.aidl \
media/java/android/media/IAudioFocusDispatcher.aidl \
media/java/android/media/IAudioRoutesObserver.aidl \
media/java/android/media/IAudioRoutesObserver.aidl \
media/java/android/media/IMediaScannerListener.aidl \
media/java/android/media/IMediaScannerService.aidl \
media/java/android/media/IRemoteControlClient.aidl \
media/java/android/media/IRemoteControlDisplay.aidl \
media/java/android/media/IRemoteVolumeObserver.aidl \
media/java/android/media/IRingtonePlayer.aidl \
telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \

View File

@@ -2152,19 +2152,6 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_USB_AUDIO_DEVICE_PLUG =
"android.intent.action.USB_AUDIO_DEVICE_PLUG";
/**
* @hide (to be un-hidden)
* Broadcast Action: the volume handled by the receiver should be updated based on the
* mutually exclusive extras, {@link #EXTRA_VOLUME_UPDATE_DIRECTION}
* and {@link #EXTRA_VOLUME_UPDATE_VALUE}.
*
* @see #EXTRA_VOLUME_UPDATE_DIRECTION
* @see #EXTRA_VOLUME_UPDATE_VALUE
* @see android.media.RemoteControlClient
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_VOLUME_UPDATE = "android.intent.action.VOLUME_UPDATE";
/**
* <p>Broadcast Action: The user has switched on advanced settings in the settings app:</p>
* <ul>
@@ -2854,26 +2841,6 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_USERID =
"android.intent.extra.user_id";
/**
* @hide (to be un-hidden)
* 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.
* @see #ACTION_VOLUME_UPDATE
*/
public static final String EXTRA_VOLUME_UPDATE_DIRECTION =
"android.intent.extra.VOLUME_UPDATE_DIRECTION";
/**
* @hide (to be un-hidden)
* An integer indicating the new volume value, always between 0 and the value set for
* {@link RemoteControlClient#PLAYBACKINFO_VOLUME_MAX} with
* {@link RemoteControlClient#setPlaybackInformation(int, int)}
* @see #ACTION_VOLUME_UPDATE
*/
public static final String EXTRA_VOLUME_UPDATE_VALUE =
"android.intent.extra.VOLUME_UPDATE_VALUE";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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;