MediaSessionRecord.java revision 8ae0f34db936a649ddaf9cdd086c224f6514efeb
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.media; 18 19import android.content.Intent; 20import android.media.session.IMediaController; 21import android.media.session.IMediaControllerCallback; 22import android.media.session.IMediaSession; 23import android.media.session.IMediaSessionCallback; 24import android.media.session.MediaMetadata; 25import android.media.session.PlaybackState; 26import android.media.Rating; 27import android.os.Bundle; 28import android.os.Handler; 29import android.os.IBinder; 30import android.os.Looper; 31import android.os.Message; 32import android.os.RemoteException; 33import android.os.ResultReceiver; 34import android.util.Log; 35import android.util.Slog; 36import android.view.KeyEvent; 37 38import java.util.ArrayList; 39import java.util.List; 40 41/** 42 * This is the system implementation of a Session. Apps will interact with the 43 * MediaSession wrapper class instead. 44 */ 45public class MediaSessionRecord implements IBinder.DeathRecipient { 46 private static final String TAG = "MediaSessionImpl"; 47 48 private final MessageHandler mHandler; 49 50 private final int mPid; 51 private final String mPackageName; 52 private final String mTag; 53 private final ControllerStub mController; 54 private final SessionStub mSession; 55 private final SessionCb mSessionCb; 56 private final MediaSessionService mService; 57 58 private final Object mControllerLock = new Object(); 59 private final ArrayList<IMediaControllerCallback> mControllerCallbacks = 60 new ArrayList<IMediaControllerCallback>(); 61 private final ArrayList<String> mInterfaces = new ArrayList<String>(); 62 63 private boolean mTransportPerformerEnabled = false; 64 private Bundle mRoute; 65 66 // TransportPerformer fields 67 68 private MediaMetadata mMetadata; 69 private PlaybackState mPlaybackState; 70 private int mRatingType; 71 // End TransportPerformer fields 72 73 private boolean mIsPublished = false; 74 75 public MediaSessionRecord(int pid, String packageName, IMediaSessionCallback cb, String tag, 76 MediaSessionService service, Handler handler) { 77 mPid = pid; 78 mPackageName = packageName; 79 mTag = tag; 80 mController = new ControllerStub(); 81 mSession = new SessionStub(); 82 mSessionCb = new SessionCb(cb); 83 mService = service; 84 mHandler = new MessageHandler(handler.getLooper()); 85 } 86 87 public IMediaSession getSessionBinder() { 88 return mSession; 89 } 90 91 public IMediaController getControllerBinder() { 92 return mController; 93 } 94 95 @Override 96 public void binderDied() { 97 mService.sessionDied(this); 98 } 99 100 public boolean isPublished() { 101 return mIsPublished; 102 } 103 104 private void onDestroy() { 105 mService.destroySession(this); 106 } 107 108 private void pushPlaybackStateUpdate() { 109 synchronized (mControllerLock) { 110 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 111 IMediaControllerCallback cb = mControllerCallbacks.get(i); 112 try { 113 cb.onPlaybackStateChanged(mPlaybackState); 114 } catch (RemoteException e) { 115 Log.w(TAG, "Removing dead callback in pushPlaybackStateUpdate.", e); 116 mControllerCallbacks.remove(i); 117 } 118 } 119 } 120 } 121 122 private void pushMetadataUpdate() { 123 synchronized (mControllerLock) { 124 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 125 IMediaControllerCallback cb = mControllerCallbacks.get(i); 126 try { 127 cb.onMetadataChanged(mMetadata); 128 } catch (RemoteException e) { 129 Log.w(TAG, "Removing dead callback in pushMetadataUpdate.", e); 130 mControllerCallbacks.remove(i); 131 } 132 } 133 } 134 } 135 136 private void pushRouteUpdate() { 137 synchronized (mControllerLock) { 138 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 139 IMediaControllerCallback cb = mControllerCallbacks.get(i); 140 try { 141 cb.onRouteChanged(mRoute); 142 } catch (RemoteException e) { 143 Log.w(TAG, "Removing dead callback in pushRouteUpdate.", e); 144 mControllerCallbacks.remove(i); 145 } 146 } 147 } 148 } 149 150 private void pushEvent(String event, Bundle data) { 151 synchronized (mControllerLock) { 152 for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { 153 IMediaControllerCallback cb = mControllerCallbacks.get(i); 154 try { 155 cb.onEvent(event, data); 156 } catch (RemoteException e) { 157 Log.w(TAG, "Removing dead callback in pushRouteUpdate.", e); 158 mControllerCallbacks.remove(i); 159 } 160 } 161 } 162 } 163 164 private final class SessionStub extends IMediaSession.Stub { 165 166 @Override 167 public void destroy() { 168 onDestroy(); 169 } 170 171 @Override 172 public void sendEvent(String event, Bundle data) { 173 mHandler.post(MessageHandler.MSG_SEND_EVENT, event, data); 174 } 175 176 @Override 177 public IMediaController getMediaController() { 178 return mController; 179 } 180 181 @Override 182 public void setRouteState(Bundle routeState) { 183 } 184 185 @Override 186 public void setRoute(Bundle mediaRouteDescriptor) { 187 mRoute = mediaRouteDescriptor; 188 mHandler.post(MessageHandler.MSG_UPDATE_ROUTE); 189 } 190 191 @Override 192 public void publish() { 193 mIsPublished = true; // TODO push update to service 194 } 195 @Override 196 public void setTransportPerformerEnabled() { 197 mTransportPerformerEnabled = true; 198 } 199 200 @Override 201 public List<String> getSupportedInterfaces() { 202 return mInterfaces; 203 } 204 205 @Override 206 public void setMetadata(MediaMetadata metadata) { 207 mMetadata = metadata; 208 mHandler.post(MessageHandler.MSG_UPDATE_METADATA); 209 } 210 211 @Override 212 public void setPlaybackState(PlaybackState state) { 213 mPlaybackState = state; 214 mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE); 215 } 216 217 @Override 218 public void setRatingType(int type) { 219 mRatingType = type; 220 } 221 } 222 223 class SessionCb { 224 private final IMediaSessionCallback mCb; 225 226 public SessionCb(IMediaSessionCallback cb) { 227 mCb = cb; 228 } 229 230 public void sendMediaButton(KeyEvent keyEvent) { 231 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 232 mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 233 try { 234 mCb.onMediaButton(mediaButtonIntent); 235 } catch (RemoteException e) { 236 Slog.e(TAG, "Remote failure in sendMediaRequest.", e); 237 } 238 } 239 240 public void sendCommand(String command, Bundle extras, ResultReceiver cb) { 241 try { 242 mCb.onCommand(command, extras, cb); 243 } catch (RemoteException e) { 244 Slog.e(TAG, "Remote failure in sendCommand.", e); 245 } 246 } 247 248 public void play() { 249 try { 250 mCb.onPlay(); 251 } catch (RemoteException e) { 252 Slog.e(TAG, "Remote failure in play.", e); 253 } 254 } 255 256 public void pause() { 257 try { 258 mCb.onPause(); 259 } catch (RemoteException e) { 260 Slog.e(TAG, "Remote failure in pause.", e); 261 } 262 } 263 264 public void stop() { 265 try { 266 mCb.onStop(); 267 } catch (RemoteException e) { 268 Slog.e(TAG, "Remote failure in stop.", e); 269 } 270 } 271 272 public void next() { 273 try { 274 mCb.onNext(); 275 } catch (RemoteException e) { 276 Slog.e(TAG, "Remote failure in next.", e); 277 } 278 } 279 280 public void previous() { 281 try { 282 mCb.onPrevious(); 283 } catch (RemoteException e) { 284 Slog.e(TAG, "Remote failure in previous.", e); 285 } 286 } 287 288 public void fastForward() { 289 try { 290 mCb.onFastForward(); 291 } catch (RemoteException e) { 292 Slog.e(TAG, "Remote failure in fastForward.", e); 293 } 294 } 295 296 public void rewind() { 297 try { 298 mCb.onRewind(); 299 } catch (RemoteException e) { 300 Slog.e(TAG, "Remote failure in rewind.", e); 301 } 302 } 303 304 public void seekTo(long pos) { 305 try { 306 mCb.onSeekTo(pos); 307 } catch (RemoteException e) { 308 Slog.e(TAG, "Remote failure in seekTo.", e); 309 } 310 } 311 312 public void rate(Rating rating) { 313 try { 314 mCb.onRate(rating); 315 } catch (RemoteException e) { 316 Slog.e(TAG, "Remote failure in rate.", e); 317 } 318 } 319 } 320 321 class ControllerStub extends IMediaController.Stub { 322 @Override 323 public void sendCommand(String command, Bundle extras, ResultReceiver cb) 324 throws RemoteException { 325 mSessionCb.sendCommand(command, extras, cb); 326 } 327 328 @Override 329 public void sendMediaButton(KeyEvent mediaButtonIntent) { 330 mSessionCb.sendMediaButton(mediaButtonIntent); 331 } 332 333 @Override 334 public void registerCallbackListener(IMediaControllerCallback cb) { 335 synchronized (mControllerLock) { 336 if (!mControllerCallbacks.contains(cb)) { 337 mControllerCallbacks.add(cb); 338 } 339 } 340 } 341 342 @Override 343 public void unregisterCallbackListener(IMediaControllerCallback cb) 344 throws RemoteException { 345 synchronized (mControllerLock) { 346 mControllerCallbacks.remove(cb); 347 } 348 } 349 350 @Override 351 public void play() throws RemoteException { 352 mSessionCb.play(); 353 } 354 355 @Override 356 public void pause() throws RemoteException { 357 mSessionCb.pause(); 358 } 359 360 @Override 361 public void stop() throws RemoteException { 362 mSessionCb.stop(); 363 } 364 365 @Override 366 public void next() throws RemoteException { 367 mSessionCb.next(); 368 } 369 370 @Override 371 public void previous() throws RemoteException { 372 mSessionCb.previous(); 373 } 374 375 @Override 376 public void fastForward() throws RemoteException { 377 mSessionCb.fastForward(); 378 } 379 380 @Override 381 public void rewind() throws RemoteException { 382 mSessionCb.rewind(); 383 } 384 385 @Override 386 public void seekTo(long pos) throws RemoteException { 387 mSessionCb.seekTo(pos); 388 } 389 390 @Override 391 public void rate(Rating rating) throws RemoteException { 392 mSessionCb.rate(rating); 393 } 394 395 396 @Override 397 public MediaMetadata getMetadata() { 398 return mMetadata; 399 } 400 401 @Override 402 public PlaybackState getPlaybackState() { 403 return mPlaybackState; 404 } 405 406 @Override 407 public int getRatingType() { 408 return mRatingType; 409 } 410 411 @Override 412 public boolean isTransportControlEnabled() throws RemoteException { 413 return mTransportPerformerEnabled; 414 } 415 } 416 417 private class MessageHandler extends Handler { 418 private static final int MSG_UPDATE_METADATA = 1; 419 private static final int MSG_UPDATE_PLAYBACK_STATE = 2; 420 private static final int MSG_UPDATE_ROUTE = 3; 421 private static final int MSG_SEND_EVENT = 4; 422 423 public MessageHandler(Looper looper) { 424 super(looper); 425 } 426 @Override 427 public void handleMessage(Message msg) { 428 switch (msg.what) { 429 case MSG_UPDATE_METADATA: 430 pushMetadataUpdate(); 431 break; 432 case MSG_UPDATE_PLAYBACK_STATE: 433 pushPlaybackStateUpdate(); 434 break; 435 case MSG_UPDATE_ROUTE: 436 pushRouteUpdate(); 437 break; 438 case MSG_SEND_EVENT: 439 pushEvent((String) msg.obj, msg.getData()); 440 break; 441 } 442 } 443 444 public void post(int what) { 445 post(what, null); 446 } 447 448 public void post(int what, Object obj) { 449 obtainMessage(what, obj).sendToTarget(); 450 } 451 452 public void post(int what, Object obj, Bundle data) { 453 Message msg = obtainMessage(what, obj); 454 msg.setData(data); 455 msg.sendToTarget(); 456 } 457 } 458 459} 460