summaryrefslogtreecommitdiffstats
path: root/media/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'media/java/android')
-rw-r--r--media/java/android/media/AudioManager.java2
-rw-r--r--media/java/android/media/AudioService.java7
-rw-r--r--media/java/android/media/IAudioService.aidl2
-rw-r--r--media/java/android/media/MediaRouter.java161
-rw-r--r--media/java/android/media/session/IActiveSessionsListener.aidl26
-rw-r--r--media/java/android/media/session/ISessionController.aidl2
-rw-r--r--media/java/android/media/session/ISessionManager.aidl4
-rw-r--r--media/java/android/media/session/MediaController.java15
-rw-r--r--media/java/android/media/session/MediaSession.java44
-rw-r--r--media/java/android/media/session/MediaSessionInfo.aidl18
-rw-r--r--media/java/android/media/session/MediaSessionInfo.java12
-rw-r--r--media/java/android/media/session/MediaSessionManager.java75
-rw-r--r--media/java/android/media/session/MediaSessionToken.java2
-rw-r--r--media/java/android/media/session/RemoteVolumeProvider.java15
-rw-r--r--media/java/android/media/tv/TvContract.java2
-rw-r--r--media/java/android/media/tv/TvInputInfo.java34
-rw-r--r--media/java/android/media/tv/TvView.java44
17 files changed, 410 insertions, 55 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 88756d7..c3d5d94 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1017,7 +1017,7 @@ public class AudioManager {
public void setMasterMute(boolean state, int flags) {
IAudioService service = getService();
try {
- service.setMasterMute(state, flags, mICallBack);
+ service.setMasterMute(state, flags, mContext.getOpPackageName(), mICallBack);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setMasterMute", e);
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 74f39b7..2f782cc 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1321,11 +1321,16 @@ public class AudioService extends IAudioService.Stub {
}
/** @see AudioManager#setMasterMute(boolean, int) */
- public void setMasterMute(boolean state, int flags, IBinder cb) {
+ public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) {
if (mUseFixedVolume) {
return;
}
+ if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+
if (state != AudioSystem.getMasterMute()) {
AudioSystem.setMasterMute(state);
// Post a persist master volume msg
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index e59623b..ba3cfb6 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -62,7 +62,7 @@ interface IAudioService {
boolean isStreamMute(int streamType);
- void setMasterMute(boolean state, int flags, IBinder cb);
+ void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb);
boolean isMasterMute();
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index a4d491d8..1da0215 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -28,6 +28,8 @@ import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.WifiDisplay;
import android.hardware.display.WifiDisplayStatus;
+import android.media.session.MediaSession;
+import android.media.session.RemoteVolumeProvider;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
@@ -58,6 +60,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class MediaRouter {
private static final String TAG = "MediaRouter";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean USE_SESSIONS = true;
static class Static implements DisplayManager.DisplayListener {
final Context mAppContext;
@@ -1141,7 +1144,7 @@ public class MediaRouter {
public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) {
return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable);
}
-
+
/**
* Create a new route category. Each route must belong to a category.
*
@@ -1980,6 +1983,7 @@ public class MediaRouter {
*/
public static class UserRouteInfo extends RouteInfo {
RemoteControlClient mRcc;
+ SessionVolumeProvider mSvp;
UserRouteInfo(RouteCategory category) {
super(category);
@@ -1996,7 +2000,7 @@ public class MediaRouter {
mName = name;
routeUpdated();
}
-
+
/**
* Set the user-visible name of this route.
* <p>
@@ -2100,7 +2104,11 @@ public class MediaRouter {
public void setPlaybackType(int type) {
if (mPlaybackType != type) {
mPlaybackType = type;
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type);
+ if (USE_SESSIONS) {
+ configureSessionVolume();
+ } else {
+ setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type);
+ }
}
}
@@ -2113,8 +2121,12 @@ public class MediaRouter {
public void setVolumeHandling(int volumeHandling) {
if (mVolumeHandling != volumeHandling) {
mVolumeHandling = volumeHandling;
- setPlaybackInfoOnRcc(
- RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling);
+ if (USE_SESSIONS) {
+ configureSessionVolume();
+ } else {
+ setPlaybackInfoOnRcc(
+ RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling);
+ }
}
}
@@ -2127,7 +2139,13 @@ public class MediaRouter {
volume = Math.max(0, Math.min(volume, getVolumeMax()));
if (mVolume != volume) {
mVolume = volume;
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume);
+ if (USE_SESSIONS) {
+ if (mSvp != null) {
+ mSvp.notifyVolumeChanged();
+ }
+ } else {
+ setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume);
+ }
dispatchRouteVolumeChanged(this);
if (mGroup != null) {
mGroup.memberVolumeChanged(this);
@@ -2166,7 +2184,11 @@ public class MediaRouter {
public void setVolumeMax(int volumeMax) {
if (mVolumeMax != volumeMax) {
mVolumeMax = volumeMax;
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax);
+ if (USE_SESSIONS) {
+ configureSessionVolume();
+ } else {
+ setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax);
+ }
}
}
@@ -2177,29 +2199,76 @@ public class MediaRouter {
public void setPlaybackStream(int stream) {
if (mPlaybackStream != stream) {
mPlaybackStream = stream;
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream);
+ if (USE_SESSIONS) {
+ configureSessionVolume();
+ } else {
+ 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);
+ if (USE_SESSIONS) {
+ configureSessionVolume();
+ } else {
+ 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 configureSessionVolume() {
+ if (mRcc == null) {
+ if (DEBUG) {
+ Log.d(TAG, "No Rcc to configure volume for route " + mName);
+ }
+ return;
+ }
+ MediaSession session = mRcc.getMediaSession();
+ if (session == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Rcc has no session to configure volume");
+ }
+ return;
+ }
+ if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
+ int volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_FIXED;
+ switch (mVolumeHandling) {
+ case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
+ volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+ break;
+ case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
+ default:
+ break;
}
+ // Only register a new listener if necessary
+ if (mSvp == null || mSvp.getVolumeControl() != volumeControl
+ || mSvp.getMaxVolume() != mVolumeMax) {
+ mSvp = new SessionVolumeProvider(volumeControl, mVolumeMax);
+ session.setPlaybackToRemote(mSvp);
+ }
+ } else {
+ // We only know how to handle local and remote, fall back to local if not remote.
+ session.setPlaybackToLocal(mPlaybackStream);
+ mSvp = null;
}
}
@@ -2208,6 +2277,42 @@ public class MediaRouter {
mRcc.setPlaybackInformation(what, value);
}
}
+
+ class SessionVolumeProvider extends RemoteVolumeProvider {
+
+ public SessionVolumeProvider(int volumeControl, int maxVolume) {
+ super(volumeControl, maxVolume);
+ }
+
+ @Override
+ public int onGetCurrentVolume() {
+ return mVcb == null ? 0 : mVcb.route.mVolume;
+ }
+
+ @Override
+ public void onSetVolumeTo(final int volume) {
+ sStatic.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mVcb != null) {
+ mVcb.vcb.onVolumeSetRequest(mVcb.route, volume);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onAdjustVolumeBy(final int delta) {
+ sStatic.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mVcb != null) {
+ mVcb.vcb.onVolumeUpdateRequest(mVcb.route, delta);
+ }
+ }
+ });
+ }
+ }
}
/**
@@ -2504,17 +2609,17 @@ public class MediaRouter {
public CharSequence getName() {
return getName(sStatic.mResources);
}
-
+
/**
* Return the properly localized/configuration dependent name of this RouteCategory.
- *
+ *
* @param context Context to resolve name resources
* @return the name of this route category
*/
public CharSequence getName(Context context) {
return getName(context.getResources());
}
-
+
CharSequence getName(Resources res) {
if (mNameResId != 0) {
return res.getText(mNameResId);
diff --git a/media/java/android/media/session/IActiveSessionsListener.aidl b/media/java/android/media/session/IActiveSessionsListener.aidl
new file mode 100644
index 0000000..e5e24bc
--- /dev/null
+++ b/media/java/android/media/session/IActiveSessionsListener.aidl
@@ -0,0 +1,26 @@
+/* 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.session;
+
+import android.media.session.MediaSessionToken;
+
+/**
+ * Listens for changes to the list of active sessions.
+ * @hide
+ */
+oneway interface IActiveSessionsListener {
+ void onActiveSessionsChanged(in List<MediaSessionToken> sessions);
+} \ No newline at end of file
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 9ce0692..7c03907 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -19,6 +19,7 @@ import android.content.Intent;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.session.ISessionControllerCallback;
+import android.media.session.MediaSessionInfo;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -35,6 +36,7 @@ interface ISessionController {
void unregisterCallbackListener(in ISessionControllerCallback cb);
boolean isTransportControlEnabled();
void showRoutePicker();
+ MediaSessionInfo getSessionInfo();
// These commands are for the TransportController
void play();
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 6d9888f..bd1fa85 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.session.IActiveSessionsListener;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.os.Bundle;
@@ -30,4 +31,7 @@ interface ISessionManager {
List<IBinder> getSessions(in ComponentName compName, int userId);
void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock);
void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags);
+ void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
+ int userId);
+ void removeSessionsListener(in IActiveSessionsListener listener);
} \ 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 caff1ad..57a0a54 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -246,6 +246,21 @@ public final class MediaController {
}
}
+ /**
+ * Get the info for the session this controller is connected to.
+ *
+ * @return The session info for the connected session.
+ * @hide
+ */
+ public MediaSessionInfo getSessionInfo() {
+ try {
+ return mSessionBinder.getSessionInfo();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in getSessionInfo.", e);
+ }
+ return null;
+ }
+
/*
* @hide
*/
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 7972639..4ba1351 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -277,6 +277,7 @@ public final class MediaSession {
throw new IllegalArgumentException("volumeProvider may not be null!");
}
mVolumeProvider = volumeProvider;
+ volumeProvider.setSession(this);
try {
mBinder.configureVolumeHandling(VOLUME_TYPE_REMOTE, volumeProvider.getVolumeControl(),
@@ -522,6 +523,24 @@ public final class MediaSession {
}
}
+ /**
+ * Notify the system that the remove volume changed.
+ *
+ * @param provider The provider that is handling volume changes.
+ * @hide
+ */
+ void notifyRemoteVolumeChanged(RemoteVolumeProvider provider) {
+ if (provider == null || provider != mVolumeProvider) {
+ Log.w(TAG, "Received update from stale volume provider");
+ return;
+ }
+ try {
+ mBinder.setCurrentVolume(provider.onGetCurrentVolume());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in notifyVolumeChanged", e);
+ }
+ }
+
private void dispatchPlay() {
postToTransportCallbacks(TransportMessageHandler.MSG_PLAY);
}
@@ -963,27 +982,26 @@ public final class MediaSession {
@Override
public void onRouteStateChange(int state) throws RemoteException {
// TODO
-
}
- /*
- * (non-Javadoc)
- * @see android.media.session.ISessionCallback#onAdjustVolumeBy(int)
- */
@Override
public void onAdjustVolumeBy(int delta) throws RemoteException {
- // TODO(epastern): Auto-generated method stub
-
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ if (session.mVolumeProvider != null) {
+ session.mVolumeProvider.onAdjustVolumeBy(delta);
+ }
+ }
}
- /*
- * (non-Javadoc)
- * @see android.media.session.ISessionCallback#onSetVolumeTo(int)
- */
@Override
public void onSetVolumeTo(int value) throws RemoteException {
- // TODO(epastern): Auto-generated method stub
-
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ if (session.mVolumeProvider != null) {
+ session.mVolumeProvider.onSetVolumeTo(value);
+ }
+ }
}
}
diff --git a/media/java/android/media/session/MediaSessionInfo.aidl b/media/java/android/media/session/MediaSessionInfo.aidl
new file mode 100644
index 0000000..63dca9a
--- /dev/null
+++ b/media/java/android/media/session/MediaSessionInfo.aidl
@@ -0,0 +1,18 @@
+/* Copyright 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.session;
+
+parcelable MediaSessionInfo;
diff --git a/media/java/android/media/session/MediaSessionInfo.java b/media/java/android/media/session/MediaSessionInfo.java
index f701211..4dc1c09 100644
--- a/media/java/android/media/session/MediaSessionInfo.java
+++ b/media/java/android/media/session/MediaSessionInfo.java
@@ -26,18 +26,21 @@ import android.os.Parcelable;
public final class MediaSessionInfo implements Parcelable {
private final String mId;
private final String mPackageName;
+ private final int mPid;
/**
* @hide
*/
- public MediaSessionInfo(String id, String packageName) {
+ public MediaSessionInfo(String id, String packageName, int pid) {
mId = id;
mPackageName = packageName;
+ mPid = pid;
}
private MediaSessionInfo(Parcel in) {
mId = in.readString();
mPackageName = in.readString();
+ mPid = in.readInt();
}
/**
@@ -58,9 +61,13 @@ public final class MediaSessionInfo implements Parcelable {
return mId;
}
+ public int getPid() {
+ return mPid;
+ }
+
@Override
public String toString() {
- return "SessionInfo {id=" + mId + ", pkg=" + mPackageName + "}";
+ return "SessionInfo {id=" + mId + ", pkg=" + mPackageName + ", pid=" + mPid + "}";
}
@Override
@@ -72,6 +79,7 @@ public final class MediaSessionInfo implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mId);
dest.writeString(mPackageName);
+ dest.writeInt(mPid);
}
public static final Parcelable.Creator<MediaSessionInfo> CREATOR
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 9e8b0d3..291bfc8 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -142,6 +142,50 @@ public final class MediaSessionManager {
}
/**
+ * Add a listener to be notified when the list of active sessions
+ * changes.This requires the
+ * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
+ * the calling app. You may also retrieve this list if your app is an
+ * enabled notification listener using the
+ * {@link NotificationListenerService} APIs, in which case you must pass the
+ * {@link ComponentName} of your enabled listener.
+ *
+ * @param sessionListener The listener to add.
+ * @param notificationListener The enabled notification listener component.
+ * May be null.
+ * @param userId The userId to listen for changes on.
+ * @hide
+ */
+ public void addActiveSessionsListener(SessionListener sessionListener,
+ ComponentName notificationListener, int userId) {
+ if (sessionListener == null) {
+ throw new IllegalArgumentException("listener may not be null");
+ }
+ try {
+ mService.addSessionsListener(sessionListener.mStub, notificationListener, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in addActiveSessionsListener.", e);
+ }
+ }
+
+ /**
+ * Stop receiving active sessions updates on the specified listener.
+ *
+ * @param listener The listener to remove.
+ * @hide
+ */
+ public void removeActiveSessionsListener(SessionListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener may not be null");
+ }
+ try {
+ mService.removeSessionsListener(listener.mStub);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in removeActiveSessionsListener.", e);
+ }
+ }
+
+ /**
* Send a media key event. The receiver will be selected automatically.
*
* @param keyEvent The KeyEvent to send.
@@ -184,4 +228,35 @@ public final class MediaSessionManager {
Log.e(TAG, "Failed to send adjust volume.", e);
}
}
+
+ /**
+ * Listens for changes to the list of active sessions. This can be added
+ * using {@link #addActiveSessionsListener}.
+ *
+ * @hide
+ */
+ public static abstract class SessionListener {
+ /**
+ * Called when the list of active sessions has changed. This can be due
+ * to a session being added or removed or the order of sessions
+ * changing.
+ *
+ * @param controllers The updated list of controllers for the user that
+ * changed.
+ */
+ public abstract void onActiveSessionsChanged(List<MediaController> controllers);
+
+ private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() {
+ @Override
+ public void onActiveSessionsChanged(List<MediaSessionToken> tokens)
+ throws RemoteException {
+ ArrayList<MediaController> controllers = new ArrayList<MediaController>();
+ int size = tokens.size();
+ for (int i = 0; i < size; i++) {
+ controllers.add(MediaController.fromToken(tokens.get(i)));
+ }
+ SessionListener.this.onActiveSessionsChanged(controllers);
+ }
+ };
+ }
}
diff --git a/media/java/android/media/session/MediaSessionToken.java b/media/java/android/media/session/MediaSessionToken.java
index 86f5662..e599189 100644
--- a/media/java/android/media/session/MediaSessionToken.java
+++ b/media/java/android/media/session/MediaSessionToken.java
@@ -31,7 +31,7 @@ public final class MediaSessionToken implements Parcelable {
/**
* @hide
*/
- MediaSessionToken(ISessionController binder) {
+ public MediaSessionToken(ISessionController binder) {
mBinder = binder;
}
diff --git a/media/java/android/media/session/RemoteVolumeProvider.java b/media/java/android/media/session/RemoteVolumeProvider.java
index 47f672f..606b1d7 100644
--- a/media/java/android/media/session/RemoteVolumeProvider.java
+++ b/media/java/android/media/session/RemoteVolumeProvider.java
@@ -15,6 +15,9 @@
*/
package android.media.session;
+import android.os.RemoteException;
+import android.util.Log;
+
/**
* Handles requests to adjust or set the volume on a session. This is also used
* to push volume updates back to the session after a request has been handled.
@@ -22,6 +25,7 @@ package android.media.session;
* {@link MediaSession#setPlaybackToRemote}.
*/
public abstract class RemoteVolumeProvider {
+ private static final String TAG = "RemoteVolumeProvider";
/**
* The volume is fixed and can not be modified. Requests to change volume
@@ -46,6 +50,8 @@ public abstract class RemoteVolumeProvider {
private final int mControlType;
private final int mMaxVolume;
+ private MediaSession mSession;
+
/**
* Create a new volume provider for handling volume events. You must specify
* the type of volume control and the maximum volume that can be used.
@@ -88,7 +94,7 @@ public abstract class RemoteVolumeProvider {
* Notify the system that the remote playback's volume has been changed.
*/
public final void notifyVolumeChanged() {
- // TODO
+ mSession.notifyRemoteVolumeChanged(this);
}
/**
@@ -107,4 +113,11 @@ public abstract class RemoteVolumeProvider {
*/
public void onAdjustVolumeBy(int delta) {
}
+
+ /**
+ * @hide
+ */
+ void setSession(MediaSession session) {
+ mSession = session;
+ }
} \ No newline at end of file
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 5e650c2..52045d3 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -306,7 +306,7 @@ public final class TvContract {
public static final int TYPE_ATSC_C = 0x00030200;
/** The channel type for ATSC-M/H (mobile/handheld). */
- public static final int TYPE_ATSC_M_H = 0x00030200;
+ public static final int TYPE_ATSC_M_H = 0x00030300;
/** The channel type for ISDB-T (terrestrial). */
public static final int TYPE_ISDB_T = 0x00040000;
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 9525c08..868c5bf 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -46,6 +46,27 @@ public final class TvInputInfo implements Parcelable {
private static final String TAG = "TvInputInfo";
/**
+ * TV input type: the TV input service is not handling input from hardware. For example,
+ * services showing streaming from the internet falls into this type.
+ */
+ public static final int TYPE_VIRTUAL = 0;
+
+ // Should be in sync with hardware/libhardware/include/hardware/tv_input.h
+
+ /**
+ * TV input type: the TV input service is HDMI. (e.g. HDMI 1)
+ */
+ public static final int TYPE_HDMI = 1;
+ /**
+ * TV input type: the TV input service is a tuner. (e.g. terrestrial tuner)
+ */
+ public static final int TYPE_TUNER = 2;
+ /**
+ * TV input type: the TV input service is stateless pass-through. (e.g. RGB, composite, etc.)
+ */
+ public static final int TYPE_PASSTHROUGH = 3;
+
+ /**
* The name of the TV input service to provide to the setup activity and settings activity.
*/
public static final String EXTRA_SERVICE_NAME = "serviceName";
@@ -58,6 +79,7 @@ public final class TvInputInfo implements Parcelable {
// Attributes from XML meta data.
private String mSetupActivity;
private String mSettingsActivity;
+ private int mType;
/**
* Create a new instance of the TvInputInfo class,
@@ -105,6 +127,11 @@ public final class TvInputInfo implements Parcelable {
Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
+ si.name);
}
+ input.mType = sa.getInt(
+ com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL);
+ if (DEBUG) {
+ Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name);
+ }
sa.recycle();
return input;
@@ -179,6 +206,13 @@ public final class TvInputInfo implements Parcelable {
}
/**
+ * Returns the type of this TV input service.
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
* Loads the user-displayed label for this TV input service.
*
* @param pm Supplies a PackageManager used to load the TV input's resources.
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index c2459a6..2831d9e 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -33,12 +33,13 @@ import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.ViewGroup;
import android.view.ViewRootImpl;
/**
* View playing TV
*/
-public class TvView extends SurfaceView {
+public class TvView extends ViewGroup {
private static final String TAG = "TvView";
// STOPSHIP: Turn debugging off.
private static final boolean DEBUG = true;
@@ -57,6 +58,7 @@ public class TvView extends SurfaceView {
private final Handler mHandler = new Handler();
private TvInputManager.Session mSession;
+ private final SurfaceView mSurfaceView;
private Surface mSurface;
private boolean mOverlayViewCreated;
private Rect mOverlayViewFrame;
@@ -64,6 +66,8 @@ public class TvView extends SurfaceView {
private MySessionCallback mSessionCallback;
private TvInputListener mListener;
private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
+ private boolean mHasStreamVolume;
+ private float mStreamVolume;
private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@Override
@@ -122,7 +126,14 @@ public class TvView extends SurfaceView {
public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- getHolder().addCallback(mSurfaceHolderCallback);
+ mSurfaceView = new SurfaceView(context, attrs, defStyleAttr) {
+ @Override
+ protected void updateWindow(boolean force, boolean redrawNeeded) {
+ super.updateWindow(force, redrawNeeded);
+ relayoutSessionOverlayView();
+ }};
+ mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
+ addView(mSurfaceView);
mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
}
@@ -143,7 +154,10 @@ public class TvView extends SurfaceView {
*/
public void setStreamVolume(float volume) {
if (DEBUG) Log.d(TAG, "setStreamVolume(" + volume + ")");
+ mHasStreamVolume = true;
+ mStreamVolume = volume;
if (mSession == null) {
+ // Volume will be set once the connection has been made.
return;
}
mSession.setStreamVolume(volume);
@@ -305,11 +319,26 @@ public class TvView extends SurfaceView {
super.onDetachedFromWindow();
}
- /** @hide */
@Override
- protected void updateWindow(boolean force, boolean redrawNeeded) {
- super.updateWindow(force, redrawNeeded);
- relayoutSessionOverlayView();
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mSurfaceView.layout(0, 0, right - left, bottom - top);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec);
+ int width = mSurfaceView.getMeasuredWidth();
+ int height = mSurfaceView.getMeasuredHeight();
+ int childState = mSurfaceView.getMeasuredState();
+ setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
+ resolveSizeAndState(height, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+ mSurfaceView.setVisibility(visibility);
}
private void release() {
@@ -476,6 +505,9 @@ public class TvView extends SurfaceView {
}
createSessionOverlayView();
mSession.tune(mChannelUri);
+ if (mHasStreamVolume) {
+ mSession.setStreamVolume(mStreamVolume);
+ }
} else {
if (mListener != null) {
mListener.onError(mInputId, ERROR_BUSY);