MediaFocusControl.java revision 0b605349176e8667c52cf75ccdd33ed63398c224
1/* 2 * Copyright (C) 2013 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 android.media; 18 19import android.app.Activity; 20import android.app.ActivityManager; 21import android.app.AppOpsManager; 22import android.app.KeyguardManager; 23import android.app.PendingIntent; 24import android.app.PendingIntent.CanceledException; 25import android.app.PendingIntent.OnFinished; 26import android.content.ActivityNotFoundException; 27import android.content.BroadcastReceiver; 28import android.content.ComponentName; 29import android.content.ContentResolver; 30import android.content.Context; 31import android.content.Intent; 32import android.content.IntentFilter; 33import android.content.pm.PackageManager; 34import android.database.ContentObserver; 35import android.net.Uri; 36import android.os.Binder; 37import android.os.Bundle; 38import android.os.Handler; 39import android.os.IBinder; 40import android.os.Looper; 41import android.os.Message; 42import android.os.PowerManager; 43import android.os.RemoteException; 44import android.os.UserHandle; 45import android.os.IBinder.DeathRecipient; 46import android.provider.Settings; 47import android.speech.RecognizerIntent; 48import android.telephony.PhoneStateListener; 49import android.telephony.TelephonyManager; 50import android.util.Log; 51import android.util.Slog; 52import android.view.KeyEvent; 53 54import java.io.FileDescriptor; 55import java.io.PrintWriter; 56import java.util.ArrayList; 57import java.util.Iterator; 58import java.util.Stack; 59 60/** 61 * @hide 62 * 63 */ 64public class MediaFocusControl implements OnFinished { 65 66 private static final String TAG = "MediaFocusControl"; 67 68 /** Debug remote control client/display feature */ 69 protected static final boolean DEBUG_RC = false; 70 /** Debug volumes */ 71 protected static final boolean DEBUG_VOL = false; 72 73 /** Used to alter media button redirection when the phone is ringing. */ 74 private boolean mIsRinging = false; 75 76 private final PowerManager.WakeLock mMediaEventWakeLock; 77 private final MediaEventHandler mEventHandler; 78 private final Context mContext; 79 private final ContentResolver mContentResolver; 80 private final VolumeController mVolumeController; 81 private final BroadcastReceiver mReceiver = new PackageIntentsReceiver(); 82 private final AppOpsManager mAppOps; 83 private final KeyguardManager mKeyguardManager; 84 private final AudioService mAudioService; 85 private final NotificationListenerObserver mNotifListenerObserver; 86 87 protected MediaFocusControl(Looper looper, Context cntxt, 88 VolumeController volumeCtrl, AudioService as) { 89 mEventHandler = new MediaEventHandler(looper); 90 mContext = cntxt; 91 mContentResolver = mContext.getContentResolver(); 92 mVolumeController = volumeCtrl; 93 mAudioService = as; 94 95 PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); 96 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); 97 mMainRemote = new RemotePlaybackState(-1, 98 AudioService.getMaxStreamVolume(AudioManager.STREAM_MUSIC), 99 AudioService.getMaxStreamVolume(AudioManager.STREAM_MUSIC)); 100 101 // Register for phone state monitoring 102 TelephonyManager tmgr = (TelephonyManager) 103 mContext.getSystemService(Context.TELEPHONY_SERVICE); 104 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 105 106 // Register for package addition/removal/change intent broadcasts 107 // for media button receiver persistence 108 IntentFilter pkgFilter = new IntentFilter(); 109 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 110 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 111 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); 112 pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); 113 pkgFilter.addDataScheme("package"); 114 mContext.registerReceiver(mReceiver, pkgFilter); 115 116 mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); 117 mKeyguardManager = 118 (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); 119 mNotifListenerObserver = new NotificationListenerObserver(); 120 121 mHasRemotePlayback = false; 122 mMainRemoteIsActive = false; 123 postReevaluateRemote(); 124 } 125 126 protected void dump(PrintWriter pw) { 127 dumpFocusStack(pw); 128 dumpRCStack(pw); 129 dumpRCCStack(pw); 130 dumpRCDList(pw); 131 } 132 133 //========================================================================================== 134 // Management of RemoteControlDisplay registration permissions 135 //========================================================================================== 136 private final static Uri ENABLED_NOTIFICATION_LISTENERS_URI = 137 Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); 138 139 private class NotificationListenerObserver extends ContentObserver { 140 141 NotificationListenerObserver() { 142 super(mEventHandler); 143 mContentResolver.registerContentObserver(Settings.Secure.getUriFor( 144 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS), false, this); 145 } 146 147 @Override 148 public void onChange(boolean selfChange, Uri uri) { 149 if (!ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri) || selfChange) { 150 return; 151 } 152 if (DEBUG_RC) { Log.d(TAG, "NotificationListenerObserver.onChange()"); } 153 postReevaluateRemoteControlDisplays(); 154 } 155 } 156 157 private final static int RCD_REG_FAILURE = 0; 158 private final static int RCD_REG_SUCCESS_PERMISSION = 1; 159 private final static int RCD_REG_SUCCESS_ENABLED_NOTIF = 2; 160 161 /** 162 * Checks a caller's authorization to register an IRemoteControlDisplay. 163 * Authorization is granted if one of the following is true: 164 * <ul> 165 * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL permission</li> 166 * <li>the caller's listener is one of the enabled notification listeners</li> 167 * </ul> 168 * @return RCD_REG_FAILURE if it's not safe to proceed with the IRemoteControlDisplay 169 * registration. 170 */ 171 private int checkRcdRegistrationAuthorization(ComponentName listenerComp) { 172 // MEDIA_CONTENT_CONTROL permission check 173 if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( 174 android.Manifest.permission.MEDIA_CONTENT_CONTROL)) { 175 if (DEBUG_RC) { Log.d(TAG, "ok to register Rcd: has MEDIA_CONTENT_CONTROL permission");} 176 return RCD_REG_SUCCESS_PERMISSION; 177 } 178 179 // ENABLED_NOTIFICATION_LISTENERS settings check 180 if (listenerComp != null) { 181 // this call is coming from an app, can't use its identity to read secure settings 182 final long ident = Binder.clearCallingIdentity(); 183 try { 184 final int currentUser = ActivityManager.getCurrentUser(); 185 final String enabledNotifListeners = Settings.Secure.getStringForUser( 186 mContext.getContentResolver(), 187 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, 188 currentUser); 189 if (enabledNotifListeners != null) { 190 final String[] components = enabledNotifListeners.split(":"); 191 for (int i=0; i<components.length; i++) { 192 final ComponentName component = 193 ComponentName.unflattenFromString(components[i]); 194 if (component != null) { 195 if (listenerComp.equals(component)) { 196 if (DEBUG_RC) { Log.d(TAG, "ok to register RCC: " + component + 197 " is authorized notification listener"); } 198 return RCD_REG_SUCCESS_ENABLED_NOTIF; 199 } 200 } 201 } 202 } 203 if (DEBUG_RC) { Log.d(TAG, "not ok to register RCD, " + listenerComp + 204 " is not in list of ENABLED_NOTIFICATION_LISTENERS"); } 205 } finally { 206 Binder.restoreCallingIdentity(ident); 207 } 208 } 209 210 return RCD_REG_FAILURE; 211 } 212 213 protected boolean registerRemoteController(IRemoteControlDisplay rcd, int w, int h, 214 ComponentName listenerComp) { 215 int reg = checkRcdRegistrationAuthorization(listenerComp); 216 if (reg != RCD_REG_FAILURE) { 217 registerRemoteControlDisplay_int(rcd, w, h, listenerComp); 218 return true; 219 } else { 220 Slog.w(TAG, "Access denied to process: " + Binder.getCallingPid() + 221 ", must have permission " + android.Manifest.permission.MEDIA_CONTENT_CONTROL + 222 " or be an enabled NotificationListenerService for registerRemoteController"); 223 return false; 224 } 225 } 226 227 protected boolean registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) { 228 int reg = checkRcdRegistrationAuthorization(null); 229 if (reg != RCD_REG_FAILURE) { 230 registerRemoteControlDisplay_int(rcd, w, h, null); 231 return true; 232 } else { 233 Slog.w(TAG, "Access denied to process: " + Binder.getCallingPid() + 234 ", must have permission " + android.Manifest.permission.MEDIA_CONTENT_CONTROL + 235 " to register IRemoteControlDisplay"); 236 return false; 237 } 238 } 239 240 private void postReevaluateRemoteControlDisplays() { 241 sendMsg(mEventHandler, MSG_REEVALUATE_RCD, SENDMSG_QUEUE, 0, 0, null, 0); 242 } 243 244 private void onReevaluateRemoteControlDisplays() { 245 if (DEBUG_RC) { Log.d(TAG, "onReevaluateRemoteControlDisplays()"); } 246 // read which components are enabled notification listeners 247 final int currentUser = ActivityManager.getCurrentUser(); 248 final String enabledNotifListeners = Settings.Secure.getStringForUser( 249 mContext.getContentResolver(), 250 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, 251 currentUser); 252 if (DEBUG_RC) { Log.d(TAG, " > enabled list: " + enabledNotifListeners); } 253 synchronized(mAudioFocusLock) { 254 synchronized(mRCStack) { 255 // check whether the "enable" status of each RCD with a notification listener 256 // has changed 257 final String[] enabledComponents; 258 if (enabledNotifListeners == null) { 259 enabledComponents = null; 260 } else { 261 enabledComponents = enabledNotifListeners.split(":"); 262 } 263 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); 264 while (displayIterator.hasNext()) { 265 final DisplayInfoForServer di = 266 (DisplayInfoForServer) displayIterator.next(); 267 if (di.mClientNotifListComp != null) { 268 boolean wasEnabled = di.mEnabled; 269 di.mEnabled = isComponentInStringArray(di.mClientNotifListComp, 270 enabledComponents); 271 if (wasEnabled != di.mEnabled){ 272 try { 273 // tell the RCD whether it's enabled 274 di.mRcDisplay.setEnabled(di.mEnabled); 275 // tell the RCCs about the change for this RCD 276 enableRemoteControlDisplayForClient_syncRcStack( 277 di.mRcDisplay, di.mEnabled); 278 // when enabling, refresh the information on the display 279 if (di.mEnabled) { 280 sendMsg(mEventHandler, MSG_RCDISPLAY_INIT_INFO, SENDMSG_QUEUE, 281 di.mArtworkExpectedWidth /*arg1*/, 282 di.mArtworkExpectedHeight/*arg2*/, 283 di.mRcDisplay /*obj*/, 0/*delay*/); 284 } 285 } catch (RemoteException e) { 286 Log.e(TAG, "Error en/disabling RCD: ", e); 287 } 288 } 289 } 290 } 291 } 292 } 293 } 294 295 /** 296 * @param comp a non-null ComponentName 297 * @param enabledArray may be null 298 * @return 299 */ 300 private boolean isComponentInStringArray(ComponentName comp, String[] enabledArray) { 301 if (enabledArray == null || enabledArray.length == 0) { 302 if (DEBUG_RC) { Log.d(TAG, " > " + comp + " is NOT enabled"); } 303 return false; 304 } 305 final String compString = comp.flattenToString(); 306 for (int i=0; i<enabledArray.length; i++) { 307 if (compString.equals(enabledArray[i])) { 308 if (DEBUG_RC) { Log.d(TAG, " > " + compString + " is enabled"); } 309 return true; 310 } 311 } 312 if (DEBUG_RC) { Log.d(TAG, " > " + compString + " is NOT enabled"); } 313 return false; 314 } 315 316 //========================================================================================== 317 // Internal event handling 318 //========================================================================================== 319 320 // event handler messages 321 private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 0; 322 private static final int MSG_RCDISPLAY_CLEAR = 1; 323 private static final int MSG_RCDISPLAY_UPDATE = 2; 324 private static final int MSG_REEVALUATE_REMOTE = 3; 325 private static final int MSG_RCC_NEW_PLAYBACK_INFO = 4; 326 private static final int MSG_RCC_NEW_VOLUME_OBS = 5; 327 private static final int MSG_PROMOTE_RCC = 6; 328 private static final int MSG_RCC_NEW_PLAYBACK_STATE = 7; 329 private static final int MSG_RCC_SEEK_REQUEST = 8; 330 private static final int MSG_RCC_UPDATE_METADATA = 9; 331 private static final int MSG_RCDISPLAY_INIT_INFO = 10; 332 private static final int MSG_REEVALUATE_RCD = 11; 333 334 // sendMsg() flags 335 /** If the msg is already queued, replace it with this one. */ 336 private static final int SENDMSG_REPLACE = 0; 337 /** If the msg is already queued, ignore this one and leave the old. */ 338 private static final int SENDMSG_NOOP = 1; 339 /** If the msg is already queued, queue this one and leave the old. */ 340 private static final int SENDMSG_QUEUE = 2; 341 342 private static void sendMsg(Handler handler, int msg, 343 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) { 344 345 if (existingMsgPolicy == SENDMSG_REPLACE) { 346 handler.removeMessages(msg); 347 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) { 348 return; 349 } 350 351 handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay); 352 } 353 354 private class MediaEventHandler extends Handler { 355 MediaEventHandler(Looper looper) { 356 super(looper); 357 } 358 359 @Override 360 public void handleMessage(Message msg) { 361 switch(msg.what) { 362 case MSG_PERSIST_MEDIABUTTONRECEIVER: 363 onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj ); 364 break; 365 366 case MSG_RCDISPLAY_CLEAR: 367 onRcDisplayClear(); 368 break; 369 370 case MSG_RCDISPLAY_UPDATE: 371 // msg.obj is guaranteed to be non null 372 onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1); 373 break; 374 375 case MSG_REEVALUATE_REMOTE: 376 onReevaluateRemote(); 377 break; 378 379 case MSG_RCC_NEW_PLAYBACK_INFO: 380 onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */, 381 ((Integer)msg.obj).intValue() /* value */); 382 break; 383 384 case MSG_RCC_NEW_VOLUME_OBS: 385 onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */, 386 (IRemoteVolumeObserver)msg.obj /* rvo */); 387 break; 388 389 case MSG_RCC_NEW_PLAYBACK_STATE: 390 onNewPlaybackStateForRcc(msg.arg1 /* rccId */, 391 msg.arg2 /* state */, 392 (RccPlaybackState)msg.obj /* newState */); 393 break; 394 395 case MSG_RCC_SEEK_REQUEST: 396 onSetRemoteControlClientPlaybackPosition( 397 msg.arg1 /* generationId */, ((Long)msg.obj).longValue() /* timeMs */); 398 break; 399 400 case MSG_RCC_UPDATE_METADATA: 401 onUpdateRemoteControlClientMetadata(msg.arg1 /*genId*/, msg.arg2 /*key*/, 402 (Rating) msg.obj /* value */); 403 break; 404 405 case MSG_PROMOTE_RCC: 406 onPromoteRcc(msg.arg1); 407 break; 408 409 case MSG_RCDISPLAY_INIT_INFO: 410 // msg.obj is guaranteed to be non null 411 onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/, 412 msg.arg1/*w*/, msg.arg2/*h*/); 413 break; 414 415 case MSG_REEVALUATE_RCD: 416 onReevaluateRemoteControlDisplays(); 417 break; 418 } 419 } 420 } 421 422 423 //========================================================================================== 424 // AudioFocus 425 //========================================================================================== 426 427 /* constant to identify focus stack entry that is used to hold the focus while the phone 428 * is ringing or during a call. Used by com.android.internal.telephony.CallManager when 429 * entering and exiting calls. 430 */ 431 protected final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls"; 432 433 private final static Object mAudioFocusLock = new Object(); 434 435 private final static Object mRingingLock = new Object(); 436 437 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 438 @Override 439 public void onCallStateChanged(int state, String incomingNumber) { 440 if (state == TelephonyManager.CALL_STATE_RINGING) { 441 //Log.v(TAG, " CALL_STATE_RINGING"); 442 synchronized(mRingingLock) { 443 mIsRinging = true; 444 } 445 } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK) 446 || (state == TelephonyManager.CALL_STATE_IDLE)) { 447 synchronized(mRingingLock) { 448 mIsRinging = false; 449 } 450 } 451 } 452 }; 453 454 /** 455 * Discard the current audio focus owner. 456 * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign 457 * focus), remove it from the stack, and clear the remote control display. 458 */ 459 protected void discardAudioFocusOwner() { 460 synchronized(mAudioFocusLock) { 461 if (!mFocusStack.empty()) { 462 // notify the current focus owner it lost focus after removing it from stack 463 final FocusRequester exFocusOwner = mFocusStack.pop(); 464 exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS); 465 exFocusOwner.release(); 466 // clear RCD 467 synchronized(mRCStack) { 468 clearRemoteControlDisplay_syncAfRcs(); 469 } 470 } 471 } 472 } 473 474 private void notifyTopOfAudioFocusStack() { 475 // notify the top of the stack it gained focus 476 if (!mFocusStack.empty()) { 477 if (canReassignAudioFocus()) { 478 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN); 479 } 480 } 481 } 482 483 /** 484 * Focus is requested, propagate the associated loss throughout the stack. 485 * @param focusGain the new focus gain that will later be added at the top of the stack 486 */ 487 private void propagateFocusLossFromGain_syncAf(int focusGain) { 488 // going through the audio focus stack to signal new focus, traversing order doesn't 489 // matter as all entries respond to the same external focus gain 490 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 491 while(stackIterator.hasNext()) { 492 stackIterator.next().handleExternalFocusGain(focusGain); 493 } 494 } 495 496 private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>(); 497 498 /** 499 * Helper function: 500 * Display in the log the current entries in the audio focus stack 501 */ 502 private void dumpFocusStack(PrintWriter pw) { 503 pw.println("\nAudio Focus stack entries (last is top of stack):"); 504 synchronized(mAudioFocusLock) { 505 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 506 while(stackIterator.hasNext()) { 507 stackIterator.next().dump(pw); 508 } 509 } 510 } 511 512 /** 513 * Helper function: 514 * Called synchronized on mAudioFocusLock 515 * Remove a focus listener from the focus stack. 516 * @param clientToRemove the focus listener 517 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding 518 * focus, notify the next item in the stack it gained focus. 519 */ 520 private void removeFocusStackEntry(String clientToRemove, boolean signal) { 521 // is the current top of the focus stack abandoning focus? (because of request, not death) 522 if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove)) 523 { 524 //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); 525 FocusRequester fr = mFocusStack.pop(); 526 fr.release(); 527 if (signal) { 528 // notify the new top of the stack it gained focus 529 notifyTopOfAudioFocusStack(); 530 // there's a new top of the stack, let the remote control know 531 synchronized(mRCStack) { 532 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 533 } 534 } 535 } else { 536 // focus is abandoned by a client that's not at the top of the stack, 537 // no need to update focus. 538 // (using an iterator on the stack so we can safely remove an entry after having 539 // evaluated it, traversal order doesn't matter here) 540 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 541 while(stackIterator.hasNext()) { 542 FocusRequester fr = (FocusRequester)stackIterator.next(); 543 if(fr.hasSameClient(clientToRemove)) { 544 Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " 545 + clientToRemove); 546 stackIterator.remove(); 547 fr.release(); 548 } 549 } 550 } 551 } 552 553 /** 554 * Helper function: 555 * Called synchronized on mAudioFocusLock 556 * Remove focus listeners from the focus stack for a particular client when it has died. 557 */ 558 private void removeFocusStackEntryForClient(IBinder cb) { 559 // is the owner of the audio focus part of the client to remove? 560 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && 561 mFocusStack.peek().hasSameBinder(cb); 562 // (using an iterator on the stack so we can safely remove an entry after having 563 // evaluated it, traversal order doesn't matter here) 564 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 565 while(stackIterator.hasNext()) { 566 FocusRequester fr = (FocusRequester)stackIterator.next(); 567 if(fr.hasSameBinder(cb)) { 568 Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " + cb); 569 stackIterator.remove(); 570 // the client just died, no need to unlink to its death 571 } 572 } 573 if (isTopOfStackForClientToRemove) { 574 // we removed an entry at the top of the stack: 575 // notify the new top of the stack it gained focus. 576 notifyTopOfAudioFocusStack(); 577 // there's a new top of the stack, let the remote control know 578 synchronized(mRCStack) { 579 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 580 } 581 } 582 } 583 584 /** 585 * Helper function: 586 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. 587 */ 588 private boolean canReassignAudioFocus() { 589 // focus requests are rejected during a phone call or when the phone is ringing 590 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus 591 if (!mFocusStack.isEmpty() && mFocusStack.peek().hasSameClient(IN_VOICE_COMM_FOCUS_ID)) { 592 return false; 593 } 594 return true; 595 } 596 597 /** 598 * Inner class to monitor audio focus client deaths, and remove them from the audio focus 599 * stack if necessary. 600 */ 601 protected class AudioFocusDeathHandler implements IBinder.DeathRecipient { 602 private IBinder mCb; // To be notified of client's death 603 604 AudioFocusDeathHandler(IBinder cb) { 605 mCb = cb; 606 } 607 608 public void binderDied() { 609 synchronized(mAudioFocusLock) { 610 Log.w(TAG, " AudioFocus audio focus client died"); 611 removeFocusStackEntryForClient(mCb); 612 } 613 } 614 615 public IBinder getBinder() { 616 return mCb; 617 } 618 } 619 620 protected int getCurrentAudioFocus() { 621 synchronized(mAudioFocusLock) { 622 if (mFocusStack.empty()) { 623 return AudioManager.AUDIOFOCUS_NONE; 624 } else { 625 return mFocusStack.peek().getGainRequest(); 626 } 627 } 628 } 629 630 /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int) */ 631 protected int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb, 632 IAudioFocusDispatcher fd, String clientId, String callingPackageName) { 633 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId); 634 // we need a valid binder callback for clients 635 if (!cb.pingBinder()) { 636 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); 637 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 638 } 639 640 if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(), 641 callingPackageName) != AppOpsManager.MODE_ALLOWED) { 642 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 643 } 644 645 synchronized(mAudioFocusLock) { 646 if (!canReassignAudioFocus()) { 647 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 648 } 649 650 // handle the potential premature death of the new holder of the focus 651 // (premature death == death before abandoning focus) 652 // Register for client death notification 653 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); 654 try { 655 cb.linkToDeath(afdh, 0); 656 } catch (RemoteException e) { 657 // client has already died! 658 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); 659 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 660 } 661 662 if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) { 663 // if focus is already owned by this client and the reason for acquiring the focus 664 // hasn't changed, don't do anything 665 if (mFocusStack.peek().getGainRequest() == focusChangeHint) { 666 // unlink death handler so it can be gc'ed. 667 // linkToDeath() creates a JNI global reference preventing collection. 668 cb.unlinkToDeath(afdh, 0); 669 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 670 } 671 // the reason for the audio focus request has changed: remove the current top of 672 // stack and respond as if we had a new focus owner 673 FocusRequester fr = mFocusStack.pop(); 674 fr.release(); 675 } 676 677 // focus requester might already be somewhere below in the stack, remove it 678 removeFocusStackEntry(clientId, false /* signal */); 679 680 // propagate the focus change through the stack 681 if (!mFocusStack.empty()) { 682 propagateFocusLossFromGain_syncAf(focusChangeHint); 683 } 684 685 // push focus requester at the top of the audio focus stack 686 mFocusStack.push(new FocusRequester(mainStreamType, focusChangeHint, fd, cb, 687 clientId, afdh, callingPackageName, Binder.getCallingUid())); 688 689 // there's a new top of the stack, let the remote control know 690 synchronized(mRCStack) { 691 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 692 } 693 }//synchronized(mAudioFocusLock) 694 695 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 696 } 697 698 /** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener) */ 699 protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) { 700 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId); 701 try { 702 // this will take care of notifying the new focus owner if needed 703 synchronized(mAudioFocusLock) { 704 removeFocusStackEntry(clientId, true /*signal*/); 705 } 706 } catch (java.util.ConcurrentModificationException cme) { 707 // Catching this exception here is temporary. It is here just to prevent 708 // a crash seen when the "Silent" notification is played. This is believed to be fixed 709 // but this try catch block is left just to be safe. 710 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme); 711 cme.printStackTrace(); 712 } 713 714 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 715 } 716 717 718 protected void unregisterAudioFocusClient(String clientId) { 719 synchronized(mAudioFocusLock) { 720 removeFocusStackEntry(clientId, false); 721 } 722 } 723 724 725 //========================================================================================== 726 // RemoteControl 727 //========================================================================================== 728 /** 729 * No-op if the key code for keyEvent is not a valid media key 730 * (see {@link #isValidMediaKeyEvent(KeyEvent)}) 731 * @param keyEvent the key event to send 732 */ 733 protected void dispatchMediaKeyEvent(KeyEvent keyEvent) { 734 filterMediaKeyEvent(keyEvent, false /*needWakeLock*/); 735 } 736 737 /** 738 * No-op if the key code for keyEvent is not a valid media key 739 * (see {@link #isValidMediaKeyEvent(KeyEvent)}) 740 * @param keyEvent the key event to send 741 */ 742 protected void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) { 743 filterMediaKeyEvent(keyEvent, true /*needWakeLock*/); 744 } 745 746 private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { 747 // sanity check on the incoming key event 748 if (!isValidMediaKeyEvent(keyEvent)) { 749 Log.e(TAG, "not dispatching invalid media key event " + keyEvent); 750 return; 751 } 752 // event filtering for telephony 753 synchronized(mRingingLock) { 754 synchronized(mRCStack) { 755 if ((mMediaReceiverForCalls != null) && 756 (mIsRinging || (mAudioService.getMode() == AudioSystem.MODE_IN_CALL))) { 757 dispatchMediaKeyEventForCalls(keyEvent, needWakeLock); 758 return; 759 } 760 } 761 } 762 // event filtering based on voice-based interactions 763 if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) { 764 filterVoiceInputKeyEvent(keyEvent, needWakeLock); 765 } else { 766 dispatchMediaKeyEvent(keyEvent, needWakeLock); 767 } 768 } 769 770 /** 771 * Handles the dispatching of the media button events to the telephony package. 772 * Precondition: mMediaReceiverForCalls != null 773 * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons 774 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event 775 * is dispatched. 776 */ 777 private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) { 778 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); 779 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 780 keyIntent.setPackage(mMediaReceiverForCalls.getPackageName()); 781 if (needWakeLock) { 782 mMediaEventWakeLock.acquire(); 783 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED); 784 } 785 final long ident = Binder.clearCallingIdentity(); 786 try { 787 mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL, 788 null, mKeyEventDone, mEventHandler, Activity.RESULT_OK, null, null); 789 } finally { 790 Binder.restoreCallingIdentity(ident); 791 } 792 } 793 794 /** 795 * Handles the dispatching of the media button events to one of the registered listeners, 796 * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system. 797 * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons 798 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event 799 * is dispatched. 800 */ 801 private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { 802 if (needWakeLock) { 803 mMediaEventWakeLock.acquire(); 804 } 805 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); 806 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 807 synchronized(mRCStack) { 808 if (!mRCStack.empty()) { 809 // send the intent that was registered by the client 810 try { 811 mRCStack.peek().mMediaIntent.send(mContext, 812 needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/, 813 keyIntent, this, mEventHandler); 814 } catch (CanceledException e) { 815 Log.e(TAG, "Error sending pending intent " + mRCStack.peek()); 816 e.printStackTrace(); 817 } 818 } else { 819 // legacy behavior when nobody registered their media button event receiver 820 // through AudioManager 821 if (needWakeLock) { 822 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED); 823 } 824 final long ident = Binder.clearCallingIdentity(); 825 try { 826 mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL, 827 null, mKeyEventDone, 828 mEventHandler, Activity.RESULT_OK, null, null); 829 } finally { 830 Binder.restoreCallingIdentity(ident); 831 } 832 } 833 } 834 } 835 836 /** 837 * The different actions performed in response to a voice button key event. 838 */ 839 private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1; 840 private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2; 841 private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3; 842 843 private final Object mVoiceEventLock = new Object(); 844 private boolean mVoiceButtonDown; 845 private boolean mVoiceButtonHandled; 846 847 /** 848 * Filter key events that may be used for voice-based interactions 849 * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported 850 * media buttons that can be used to trigger voice-based interactions. 851 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event 852 * is dispatched. 853 */ 854 private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { 855 if (DEBUG_RC) { 856 Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock); 857 } 858 859 int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS; 860 int keyAction = keyEvent.getAction(); 861 synchronized (mVoiceEventLock) { 862 if (keyAction == KeyEvent.ACTION_DOWN) { 863 if (keyEvent.getRepeatCount() == 0) { 864 // initial down 865 mVoiceButtonDown = true; 866 mVoiceButtonHandled = false; 867 } else if (mVoiceButtonDown && !mVoiceButtonHandled 868 && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { 869 // long-press, start voice-based interactions 870 mVoiceButtonHandled = true; 871 voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT; 872 } 873 } else if (keyAction == KeyEvent.ACTION_UP) { 874 if (mVoiceButtonDown) { 875 // voice button up 876 mVoiceButtonDown = false; 877 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { 878 voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS; 879 } 880 } 881 } 882 }//synchronized (mVoiceEventLock) 883 884 // take action after media button event filtering for voice-based interactions 885 switch (voiceButtonAction) { 886 case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS: 887 if (DEBUG_RC) Log.v(TAG, " ignore key event"); 888 break; 889 case VOICEBUTTON_ACTION_START_VOICE_INPUT: 890 if (DEBUG_RC) Log.v(TAG, " start voice-based interactions"); 891 // then start the voice-based interactions 892 startVoiceBasedInteractions(needWakeLock); 893 break; 894 case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS: 895 if (DEBUG_RC) Log.v(TAG, " send simulated key event, wakelock=" + needWakeLock); 896 sendSimulatedMediaButtonEvent(keyEvent, needWakeLock); 897 break; 898 } 899 } 900 901 private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) { 902 // send DOWN event 903 KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN); 904 dispatchMediaKeyEvent(keyEvent, needWakeLock); 905 // send UP event 906 keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP); 907 dispatchMediaKeyEvent(keyEvent, needWakeLock); 908 909 } 910 911 private class PackageIntentsReceiver extends BroadcastReceiver { 912 @Override 913 public void onReceive(Context context, Intent intent) { 914 String action = intent.getAction(); 915 if (action.equals(Intent.ACTION_PACKAGE_REMOVED) 916 || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) { 917 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 918 // a package is being removed, not replaced 919 String packageName = intent.getData().getSchemeSpecificPart(); 920 if (packageName != null) { 921 cleanupMediaButtonReceiverForPackage(packageName, true); 922 } 923 } 924 } else if (action.equals(Intent.ACTION_PACKAGE_ADDED) 925 || action.equals(Intent.ACTION_PACKAGE_CHANGED)) { 926 String packageName = intent.getData().getSchemeSpecificPart(); 927 if (packageName != null) { 928 cleanupMediaButtonReceiverForPackage(packageName, false); 929 } 930 } 931 } 932 } 933 934 protected static boolean isMediaKeyCode(int keyCode) { 935 switch (keyCode) { 936 case KeyEvent.KEYCODE_MUTE: 937 case KeyEvent.KEYCODE_HEADSETHOOK: 938 case KeyEvent.KEYCODE_MEDIA_PLAY: 939 case KeyEvent.KEYCODE_MEDIA_PAUSE: 940 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 941 case KeyEvent.KEYCODE_MEDIA_STOP: 942 case KeyEvent.KEYCODE_MEDIA_NEXT: 943 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 944 case KeyEvent.KEYCODE_MEDIA_REWIND: 945 case KeyEvent.KEYCODE_MEDIA_RECORD: 946 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: 947 case KeyEvent.KEYCODE_MEDIA_CLOSE: 948 case KeyEvent.KEYCODE_MEDIA_EJECT: 949 case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: 950 return true; 951 default: 952 return false; 953 } 954 } 955 956 private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) { 957 if (keyEvent == null) { 958 return false; 959 } 960 return MediaFocusControl.isMediaKeyCode(keyEvent.getKeyCode()); 961 } 962 963 /** 964 * Checks whether the given key code is one that can trigger the launch of voice-based 965 * interactions. 966 * @param keyCode the key code associated with the key event 967 * @return true if the key is one of the supported voice-based interaction triggers 968 */ 969 private static boolean isValidVoiceInputKeyCode(int keyCode) { 970 if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) { 971 return true; 972 } else { 973 return false; 974 } 975 } 976 977 /** 978 * Tell the system to start voice-based interactions / voice commands 979 */ 980 private void startVoiceBasedInteractions(boolean needWakeLock) { 981 Intent voiceIntent = null; 982 // select which type of search to launch: 983 // - screen on and device unlocked: action is ACTION_WEB_SEARCH 984 // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE 985 // with EXTRA_SECURE set to true if the device is securely locked 986 PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); 987 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); 988 if (!isLocked && pm.isScreenOn()) { 989 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH); 990 Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH"); 991 } else { 992 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE); 993 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE, 994 isLocked && mKeyguardManager.isKeyguardSecure()); 995 Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE"); 996 } 997 // start the search activity 998 if (needWakeLock) { 999 mMediaEventWakeLock.acquire(); 1000 } 1001 final long identity = Binder.clearCallingIdentity(); 1002 try { 1003 if (voiceIntent != null) { 1004 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1005 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 1006 mContext.startActivityAsUser(voiceIntent, UserHandle.CURRENT); 1007 } 1008 } catch (ActivityNotFoundException e) { 1009 Log.w(TAG, "No activity for search: " + e); 1010 } finally { 1011 Binder.restoreCallingIdentity(identity); 1012 if (needWakeLock) { 1013 mMediaEventWakeLock.release(); 1014 } 1015 } 1016 } 1017 1018 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number 1019 1020 // only set when wakelock was acquired, no need to check value when received 1021 private static final String EXTRA_WAKELOCK_ACQUIRED = 1022 "android.media.AudioService.WAKELOCK_ACQUIRED"; 1023 1024 public void onSendFinished(PendingIntent pendingIntent, Intent intent, 1025 int resultCode, String resultData, Bundle resultExtras) { 1026 if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) { 1027 mMediaEventWakeLock.release(); 1028 } 1029 } 1030 1031 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() { 1032 public void onReceive(Context context, Intent intent) { 1033 if (intent == null) { 1034 return; 1035 } 1036 Bundle extras = intent.getExtras(); 1037 if (extras == null) { 1038 return; 1039 } 1040 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) { 1041 mMediaEventWakeLock.release(); 1042 } 1043 } 1044 }; 1045 1046 /** 1047 * Synchronization on mCurrentRcLock always inside a block synchronized on mRCStack 1048 */ 1049 private final Object mCurrentRcLock = new Object(); 1050 /** 1051 * The one remote control client which will receive a request for display information. 1052 * This object may be null. 1053 * Access protected by mCurrentRcLock. 1054 */ 1055 private IRemoteControlClient mCurrentRcClient = null; 1056 /** 1057 * The PendingIntent associated with mCurrentRcClient. Its value is irrelevant 1058 * if mCurrentRcClient is null 1059 */ 1060 private PendingIntent mCurrentRcClientIntent = null; 1061 1062 private final static int RC_INFO_NONE = 0; 1063 private final static int RC_INFO_ALL = 1064 RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART | 1065 RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA | 1066 RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA | 1067 RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE; 1068 1069 /** 1070 * A monotonically increasing generation counter for mCurrentRcClient. 1071 * Only accessed with a lock on mCurrentRcLock. 1072 * No value wrap-around issues as we only act on equal values. 1073 */ 1074 private int mCurrentRcClientGen = 0; 1075 1076 /** 1077 * Inner class to monitor remote control client deaths, and remove the client for the 1078 * remote control stack if necessary. 1079 */ 1080 private class RcClientDeathHandler implements IBinder.DeathRecipient { 1081 final private IBinder mCb; // To be notified of client's death 1082 final private PendingIntent mMediaIntent; 1083 1084 RcClientDeathHandler(IBinder cb, PendingIntent pi) { 1085 mCb = cb; 1086 mMediaIntent = pi; 1087 } 1088 1089 public void binderDied() { 1090 Log.w(TAG, " RemoteControlClient died"); 1091 // remote control client died, make sure the displays don't use it anymore 1092 // by setting its remote control client to null 1093 registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/); 1094 // the dead client was maybe handling remote playback, reevaluate 1095 postReevaluateRemote(); 1096 } 1097 1098 public IBinder getBinder() { 1099 return mCb; 1100 } 1101 } 1102 1103 /** 1104 * A global counter for RemoteControlClient identifiers 1105 */ 1106 private static int sLastRccId = 0; 1107 1108 private class RemotePlaybackState { 1109 int mRccId; 1110 int mVolume; 1111 int mVolumeMax; 1112 int mVolumeHandling; 1113 1114 private RemotePlaybackState(int id, int vol, int volMax) { 1115 mRccId = id; 1116 mVolume = vol; 1117 mVolumeMax = volMax; 1118 mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING; 1119 } 1120 } 1121 1122 /** 1123 * Internal cache for the playback information of the RemoteControlClient whose volume gets to 1124 * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack 1125 * every time we need this info. 1126 */ 1127 private RemotePlaybackState mMainRemote; 1128 /** 1129 * Indicates whether the "main" RemoteControlClient is considered active. 1130 * Use synchronized on mMainRemote. 1131 */ 1132 private boolean mMainRemoteIsActive; 1133 /** 1134 * Indicates whether there is remote playback going on. True even if there is no "active" 1135 * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it 1136 * handles remote playback. 1137 * Use synchronized on mMainRemote. 1138 */ 1139 private boolean mHasRemotePlayback; 1140 1141 private static class RccPlaybackState { 1142 public int mState; 1143 public long mPositionMs; 1144 public float mSpeed; 1145 1146 public RccPlaybackState(int state, long positionMs, float speed) { 1147 mState = state; 1148 mPositionMs = positionMs; 1149 mSpeed = speed; 1150 } 1151 1152 public void reset() { 1153 mState = RemoteControlClient.PLAYSTATE_STOPPED; 1154 mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID; 1155 mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X; 1156 } 1157 1158 @Override 1159 public String toString() { 1160 return stateToString() + ", " + posToString() + ", " + mSpeed + "X"; 1161 } 1162 1163 private String posToString() { 1164 if (mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) { 1165 return "PLAYBACK_POSITION_INVALID"; 1166 } else if (mPositionMs == RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { 1167 return "PLAYBACK_POSITION_ALWAYS_UNKNOWN"; 1168 } else { 1169 return (String.valueOf(mPositionMs) + "ms"); 1170 } 1171 } 1172 1173 private String stateToString() { 1174 switch (mState) { 1175 case RemoteControlClient.PLAYSTATE_NONE: 1176 return "PLAYSTATE_NONE"; 1177 case RemoteControlClient.PLAYSTATE_STOPPED: 1178 return "PLAYSTATE_STOPPED"; 1179 case RemoteControlClient.PLAYSTATE_PAUSED: 1180 return "PLAYSTATE_PAUSED"; 1181 case RemoteControlClient.PLAYSTATE_PLAYING: 1182 return "PLAYSTATE_PLAYING"; 1183 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: 1184 return "PLAYSTATE_FAST_FORWARDING"; 1185 case RemoteControlClient.PLAYSTATE_REWINDING: 1186 return "PLAYSTATE_REWINDING"; 1187 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: 1188 return "PLAYSTATE_SKIPPING_FORWARDS"; 1189 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: 1190 return "PLAYSTATE_SKIPPING_BACKWARDS"; 1191 case RemoteControlClient.PLAYSTATE_BUFFERING: 1192 return "PLAYSTATE_BUFFERING"; 1193 case RemoteControlClient.PLAYSTATE_ERROR: 1194 return "PLAYSTATE_ERROR"; 1195 default: 1196 return "[invalid playstate]"; 1197 } 1198 } 1199 } 1200 1201 protected static class RemoteControlStackEntry implements DeathRecipient { 1202 public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED; 1203 final public MediaFocusControl mController; 1204 /** 1205 * The target for the ACTION_MEDIA_BUTTON events. 1206 * Always non null. 1207 */ 1208 final public PendingIntent mMediaIntent; 1209 /** 1210 * The registered media button event receiver. 1211 * Always non null. 1212 */ 1213 final public ComponentName mReceiverComponent; 1214 public IBinder mToken; 1215 public String mCallingPackageName; 1216 public int mCallingUid; 1217 /** 1218 * Provides access to the information to display on the remote control. 1219 * May be null (when a media button event receiver is registered, 1220 * but no remote control client has been registered) */ 1221 public IRemoteControlClient mRcClient; 1222 public RcClientDeathHandler mRcClientDeathHandler; 1223 /** 1224 * Information only used for non-local playback 1225 */ 1226 public int mPlaybackType; 1227 public int mPlaybackVolume; 1228 public int mPlaybackVolumeMax; 1229 public int mPlaybackVolumeHandling; 1230 public int mPlaybackStream; 1231 public RccPlaybackState mPlaybackState; 1232 public IRemoteVolumeObserver mRemoteVolumeObs; 1233 1234 public void resetPlaybackInfo() { 1235 mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL; 1236 mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; 1237 mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; 1238 mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING; 1239 mPlaybackStream = AudioManager.STREAM_MUSIC; 1240 mPlaybackState.reset(); 1241 mRemoteVolumeObs = null; 1242 } 1243 1244 /** precondition: mediaIntent != null */ 1245 public RemoteControlStackEntry(MediaFocusControl controller, PendingIntent mediaIntent, 1246 ComponentName eventReceiver, IBinder token) { 1247 mController = controller; 1248 mMediaIntent = mediaIntent; 1249 mReceiverComponent = eventReceiver; 1250 mToken = token; 1251 mCallingUid = -1; 1252 mRcClient = null; 1253 mRccId = ++sLastRccId; 1254 mPlaybackState = new RccPlaybackState( 1255 RemoteControlClient.PLAYSTATE_STOPPED, 1256 RemoteControlClient.PLAYBACK_POSITION_INVALID, 1257 RemoteControlClient.PLAYBACK_SPEED_1X); 1258 1259 resetPlaybackInfo(); 1260 if (mToken != null) { 1261 try { 1262 mToken.linkToDeath(this, 0); 1263 } catch (RemoteException e) { 1264 mController.mEventHandler.post(new Runnable() { 1265 @Override public void run() { 1266 mController.unregisterMediaButtonIntent(mMediaIntent); 1267 } 1268 }); 1269 } 1270 } 1271 } 1272 1273 public void unlinkToRcClientDeath() { 1274 if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) { 1275 try { 1276 mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0); 1277 mRcClientDeathHandler = null; 1278 } catch (java.util.NoSuchElementException e) { 1279 // not much we can do here 1280 Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()"); 1281 e.printStackTrace(); 1282 } 1283 } 1284 } 1285 1286 public void destroy() { 1287 unlinkToRcClientDeath(); 1288 if (mToken != null) { 1289 mToken.unlinkToDeath(this, 0); 1290 mToken = null; 1291 } 1292 } 1293 1294 @Override 1295 public void binderDied() { 1296 mController.unregisterMediaButtonIntent(mMediaIntent); 1297 } 1298 1299 @Override 1300 protected void finalize() throws Throwable { 1301 destroy(); // unlink exception handled inside method 1302 super.finalize(); 1303 } 1304 } 1305 1306 /** 1307 * The stack of remote control event receivers. 1308 * Code sections and methods that modify the remote control event receiver stack are 1309 * synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either 1310 * stack, audio focus or RC, can lead to a change in the remote control display 1311 */ 1312 private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>(); 1313 1314 /** 1315 * The component the telephony package can register so telephony calls have priority to 1316 * handle media button events 1317 */ 1318 private ComponentName mMediaReceiverForCalls = null; 1319 1320 /** 1321 * Helper function: 1322 * Display in the log the current entries in the remote control focus stack 1323 */ 1324 private void dumpRCStack(PrintWriter pw) { 1325 pw.println("\nRemote Control stack entries (last is top of stack):"); 1326 synchronized(mRCStack) { 1327 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 1328 while(stackIterator.hasNext()) { 1329 RemoteControlStackEntry rcse = stackIterator.next(); 1330 pw.println(" pi: " + rcse.mMediaIntent + 1331 " -- pack: " + rcse.mCallingPackageName + 1332 " -- ercvr: " + rcse.mReceiverComponent + 1333 " -- client: " + rcse.mRcClient + 1334 " -- uid: " + rcse.mCallingUid + 1335 " -- type: " + rcse.mPlaybackType + 1336 " state: " + rcse.mPlaybackState); 1337 } 1338 } 1339 } 1340 1341 /** 1342 * Helper function: 1343 * Display in the log the current entries in the remote control stack, focusing 1344 * on RemoteControlClient data 1345 */ 1346 private void dumpRCCStack(PrintWriter pw) { 1347 pw.println("\nRemote Control Client stack entries (last is top of stack):"); 1348 synchronized(mRCStack) { 1349 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 1350 while(stackIterator.hasNext()) { 1351 RemoteControlStackEntry rcse = stackIterator.next(); 1352 pw.println(" uid: " + rcse.mCallingUid + 1353 " -- id: " + rcse.mRccId + 1354 " -- type: " + rcse.mPlaybackType + 1355 " -- state: " + rcse.mPlaybackState + 1356 " -- vol handling: " + rcse.mPlaybackVolumeHandling + 1357 " -- vol: " + rcse.mPlaybackVolume + 1358 " -- volMax: " + rcse.mPlaybackVolumeMax + 1359 " -- volObs: " + rcse.mRemoteVolumeObs); 1360 } 1361 synchronized(mCurrentRcLock) { 1362 pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen); 1363 } 1364 } 1365 synchronized (mMainRemote) { 1366 pw.println("\nRemote Volume State:"); 1367 pw.println(" has remote: " + mHasRemotePlayback); 1368 pw.println(" is remote active: " + mMainRemoteIsActive); 1369 pw.println(" rccId: " + mMainRemote.mRccId); 1370 pw.println(" volume handling: " 1371 + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ? 1372 "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)")); 1373 pw.println(" volume: " + mMainRemote.mVolume); 1374 pw.println(" volume steps: " + mMainRemote.mVolumeMax); 1375 } 1376 } 1377 1378 /** 1379 * Helper function: 1380 * Display in the log the current entries in the list of remote control displays 1381 */ 1382 private void dumpRCDList(PrintWriter pw) { 1383 pw.println("\nRemote Control Display list entries:"); 1384 synchronized(mRCStack) { 1385 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); 1386 while (displayIterator.hasNext()) { 1387 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); 1388 pw.println(" IRCD: " + di.mRcDisplay + 1389 " -- w:" + di.mArtworkExpectedWidth + 1390 " -- h:" + di.mArtworkExpectedHeight + 1391 " -- wantsPosSync:" + di.mWantsPositionSync + 1392 " -- " + (di.mEnabled ? "enabled" : "disabled")); 1393 } 1394 } 1395 } 1396 1397 /** 1398 * Helper function: 1399 * Remove any entry in the remote control stack that has the same package name as packageName 1400 * Pre-condition: packageName != null 1401 */ 1402 private void cleanupMediaButtonReceiverForPackage(String packageName, boolean removeAll) { 1403 synchronized(mRCStack) { 1404 if (mRCStack.empty()) { 1405 return; 1406 } else { 1407 final PackageManager pm = mContext.getPackageManager(); 1408 RemoteControlStackEntry oldTop = mRCStack.peek(); 1409 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 1410 // iterate over the stack entries 1411 // (using an iterator on the stack so we can safely remove an entry after having 1412 // evaluated it, traversal order doesn't matter here) 1413 while(stackIterator.hasNext()) { 1414 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 1415 if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) { 1416 // a stack entry is from the package being removed, remove it from the stack 1417 stackIterator.remove(); 1418 rcse.destroy(); 1419 } else if (rcse.mReceiverComponent != null) { 1420 try { 1421 // Check to see if this receiver still exists. 1422 pm.getReceiverInfo(rcse.mReceiverComponent, 0); 1423 } catch (PackageManager.NameNotFoundException e) { 1424 // Not found -- remove it! 1425 stackIterator.remove(); 1426 rcse.destroy(); 1427 } 1428 } 1429 } 1430 if (mRCStack.empty()) { 1431 // no saved media button receiver 1432 mEventHandler.sendMessage( 1433 mEventHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, 1434 null)); 1435 } else if (oldTop != mRCStack.peek()) { 1436 // the top of the stack has changed, save it in the system settings 1437 // by posting a message to persist it; only do this however if it has 1438 // a concrete component name (is not a transient registration) 1439 RemoteControlStackEntry rcse = mRCStack.peek(); 1440 if (rcse.mReceiverComponent != null) { 1441 mEventHandler.sendMessage( 1442 mEventHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, 1443 rcse.mReceiverComponent)); 1444 } 1445 } 1446 } 1447 } 1448 } 1449 1450 /** 1451 * Helper function: 1452 * Restore remote control receiver from the system settings. 1453 */ 1454 protected void restoreMediaButtonReceiver() { 1455 String receiverName = Settings.System.getStringForUser(mContentResolver, 1456 Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT); 1457 if ((null != receiverName) && !receiverName.isEmpty()) { 1458 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName); 1459 if (eventReceiver == null) { 1460 // an invalid name was persisted 1461 return; 1462 } 1463 // construct a PendingIntent targeted to the restored component name 1464 // for the media button and register it 1465 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 1466 // the associated intent will be handled by the component being registered 1467 mediaButtonIntent.setComponent(eventReceiver); 1468 PendingIntent pi = PendingIntent.getBroadcast(mContext, 1469 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/); 1470 registerMediaButtonIntent(pi, eventReceiver, null); 1471 } 1472 } 1473 1474 /** 1475 * Helper function: 1476 * Set the new remote control receiver at the top of the RC focus stack. 1477 * Called synchronized on mAudioFocusLock, then mRCStack 1478 * precondition: mediaIntent != null 1479 * @return true if mRCStack was changed, false otherwise 1480 */ 1481 private boolean pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, 1482 ComponentName target, IBinder token) { 1483 // already at top of stack? 1484 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) { 1485 return false; 1486 } 1487 if (mAppOps.noteOp(AppOpsManager.OP_TAKE_MEDIA_BUTTONS, Binder.getCallingUid(), 1488 mediaIntent.getCreatorPackage()) != AppOpsManager.MODE_ALLOWED) { 1489 return false; 1490 } 1491 RemoteControlStackEntry rcse = null; 1492 boolean wasInsideStack = false; 1493 try { 1494 for (int index = mRCStack.size()-1; index >= 0; index--) { 1495 rcse = mRCStack.elementAt(index); 1496 if(rcse.mMediaIntent.equals(mediaIntent)) { 1497 // ok to remove element while traversing the stack since we're leaving the loop 1498 mRCStack.removeElementAt(index); 1499 wasInsideStack = true; 1500 break; 1501 } 1502 } 1503 } catch (ArrayIndexOutOfBoundsException e) { 1504 // not expected to happen, indicates improper concurrent modification 1505 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e); 1506 } 1507 if (!wasInsideStack) { 1508 rcse = new RemoteControlStackEntry(this, mediaIntent, target, token); 1509 } 1510 mRCStack.push(rcse); // rcse is never null 1511 1512 // post message to persist the default media button receiver 1513 if (target != null) { 1514 mEventHandler.sendMessage( mEventHandler.obtainMessage( 1515 MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) ); 1516 } 1517 1518 // RC stack was modified 1519 return true; 1520 } 1521 1522 /** 1523 * Helper function: 1524 * Remove the remote control receiver from the RC focus stack. 1525 * Called synchronized on mAudioFocusLock, then mRCStack 1526 * precondition: pi != null 1527 */ 1528 private void removeMediaButtonReceiver_syncAfRcs(PendingIntent pi) { 1529 try { 1530 for (int index = mRCStack.size()-1; index >= 0; index--) { 1531 final RemoteControlStackEntry rcse = mRCStack.elementAt(index); 1532 if (rcse.mMediaIntent.equals(pi)) { 1533 rcse.destroy(); 1534 // ok to remove element while traversing the stack since we're leaving the loop 1535 mRCStack.removeElementAt(index); 1536 break; 1537 } 1538 } 1539 } catch (ArrayIndexOutOfBoundsException e) { 1540 // not expected to happen, indicates improper concurrent modification 1541 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e); 1542 } 1543 } 1544 1545 /** 1546 * Helper function: 1547 * Called synchronized on mRCStack 1548 */ 1549 private boolean isCurrentRcController(PendingIntent pi) { 1550 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) { 1551 return true; 1552 } 1553 return false; 1554 } 1555 1556 private void onHandlePersistMediaButtonReceiver(ComponentName receiver) { 1557 Settings.System.putStringForUser(mContentResolver, 1558 Settings.System.MEDIA_BUTTON_RECEIVER, 1559 receiver == null ? "" : receiver.flattenToString(), 1560 UserHandle.USER_CURRENT); 1561 } 1562 1563 //========================================================================================== 1564 // Remote control display / client 1565 //========================================================================================== 1566 /** 1567 * Update the remote control displays with the new "focused" client generation 1568 */ 1569 private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration, 1570 PendingIntent newMediaIntent, boolean clearing) { 1571 synchronized(mRCStack) { 1572 if (mRcDisplays.size() > 0) { 1573 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); 1574 while (displayIterator.hasNext()) { 1575 final DisplayInfoForServer di = displayIterator.next(); 1576 try { 1577 di.mRcDisplay.setCurrentClientId( 1578 newClientGeneration, newMediaIntent, clearing); 1579 } catch (RemoteException e) { 1580 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc()",e); 1581 di.release(); 1582 displayIterator.remove(); 1583 } 1584 } 1585 } 1586 } 1587 } 1588 1589 /** 1590 * Update the remote control clients with the new "focused" client generation 1591 */ 1592 private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) { 1593 // (using an iterator on the stack so we can safely remove an entry if needed, 1594 // traversal order doesn't matter here as we update all entries) 1595 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 1596 while(stackIterator.hasNext()) { 1597 RemoteControlStackEntry se = stackIterator.next(); 1598 if ((se != null) && (se.mRcClient != null)) { 1599 try { 1600 se.mRcClient.setCurrentClientGenerationId(newClientGeneration); 1601 } catch (RemoteException e) { 1602 Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()",e); 1603 stackIterator.remove(); 1604 se.unlinkToRcClientDeath(); 1605 } 1606 } 1607 } 1608 } 1609 1610 /** 1611 * Update the displays and clients with the new "focused" client generation and name 1612 * @param newClientGeneration the new generation value matching a client update 1613 * @param newMediaIntent the media button event receiver associated with the client. 1614 * May be null, which implies there is no registered media button event receiver. 1615 * @param clearing true if the new client generation value maps to a remote control update 1616 * where the display should be cleared. 1617 */ 1618 private void setNewRcClient_syncRcsCurrc(int newClientGeneration, 1619 PendingIntent newMediaIntent, boolean clearing) { 1620 // send the new valid client generation ID to all displays 1621 setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing); 1622 // send the new valid client generation ID to all clients 1623 setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration); 1624 } 1625 1626 /** 1627 * Called when processing MSG_RCDISPLAY_CLEAR event 1628 */ 1629 private void onRcDisplayClear() { 1630 if (DEBUG_RC) Log.i(TAG, "Clear remote control display"); 1631 1632 synchronized(mRCStack) { 1633 synchronized(mCurrentRcLock) { 1634 mCurrentRcClientGen++; 1635 // synchronously update the displays and clients with the new client generation 1636 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen, 1637 null /*newMediaIntent*/, true /*clearing*/); 1638 } 1639 } 1640 } 1641 1642 /** 1643 * Called when processing MSG_RCDISPLAY_UPDATE event 1644 */ 1645 private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) { 1646 synchronized(mRCStack) { 1647 synchronized(mCurrentRcLock) { 1648 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) { 1649 if (DEBUG_RC) Log.i(TAG, "Display/update remote control "); 1650 1651 mCurrentRcClientGen++; 1652 // synchronously update the displays and clients with 1653 // the new client generation 1654 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen, 1655 rcse.mMediaIntent /*newMediaIntent*/, 1656 false /*clearing*/); 1657 1658 // tell the current client that it needs to send info 1659 try { 1660 //TODO change name to informationRequestForAllDisplays() 1661 mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags); 1662 } catch (RemoteException e) { 1663 Log.e(TAG, "Current valid remote client is dead: "+e); 1664 mCurrentRcClient = null; 1665 } 1666 } else { 1667 // the remote control display owner has changed between the 1668 // the message to update the display was sent, and the time it 1669 // gets to be processed (now) 1670 } 1671 } 1672 } 1673 } 1674 1675 /** 1676 * Called when processing MSG_RCDISPLAY_INIT_INFO event 1677 * Causes the current RemoteControlClient to send its info (metadata, playstate...) to 1678 * a single RemoteControlDisplay, NOT all of them, as with MSG_RCDISPLAY_UPDATE. 1679 */ 1680 private void onRcDisplayInitInfo(IRemoteControlDisplay newRcd, int w, int h) { 1681 synchronized(mRCStack) { 1682 synchronized(mCurrentRcLock) { 1683 if (mCurrentRcClient != null) { 1684 if (DEBUG_RC) { Log.i(TAG, "Init RCD with current info"); } 1685 try { 1686 // synchronously update the new RCD with the current client generation 1687 // and matching PendingIntent 1688 newRcd.setCurrentClientId(mCurrentRcClientGen, mCurrentRcClientIntent, 1689 false); 1690 1691 // tell the current RCC that it needs to send info, but only to the new RCD 1692 try { 1693 mCurrentRcClient.informationRequestForDisplay(newRcd, w, h); 1694 } catch (RemoteException e) { 1695 Log.e(TAG, "Current valid remote client is dead: ", e); 1696 mCurrentRcClient = null; 1697 } 1698 } catch (RemoteException e) { 1699 Log.e(TAG, "Dead display in onRcDisplayInitInfo()", e); 1700 } 1701 } 1702 } 1703 } 1704 } 1705 1706 /** 1707 * Helper function: 1708 * Called synchronized on mRCStack 1709 */ 1710 private void clearRemoteControlDisplay_syncAfRcs() { 1711 synchronized(mCurrentRcLock) { 1712 mCurrentRcClient = null; 1713 } 1714 // will cause onRcDisplayClear() to be called in AudioService's handler thread 1715 mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) ); 1716 } 1717 1718 /** 1719 * Helper function for code readability: only to be called from 1720 * checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for 1721 * this method. 1722 * Preconditions: 1723 * - called synchronized mAudioFocusLock then on mRCStack 1724 * - mRCStack.isEmpty() is false 1725 */ 1726 private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) { 1727 RemoteControlStackEntry rcse = mRCStack.peek(); 1728 int infoFlagsAboutToBeUsed = infoChangedFlags; 1729 // this is where we enforce opt-in for information display on the remote controls 1730 // with the new AudioManager.registerRemoteControlClient() API 1731 if (rcse.mRcClient == null) { 1732 //Log.w(TAG, "Can't update remote control display with null remote control client"); 1733 clearRemoteControlDisplay_syncAfRcs(); 1734 return; 1735 } 1736 synchronized(mCurrentRcLock) { 1737 if (!rcse.mRcClient.equals(mCurrentRcClient)) { 1738 // new RC client, assume every type of information shall be queried 1739 infoFlagsAboutToBeUsed = RC_INFO_ALL; 1740 } 1741 mCurrentRcClient = rcse.mRcClient; 1742 mCurrentRcClientIntent = rcse.mMediaIntent; 1743 } 1744 // will cause onRcDisplayUpdate() to be called in AudioService's handler thread 1745 mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_UPDATE, 1746 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) ); 1747 } 1748 1749 /** 1750 * Helper function: 1751 * Called synchronized on mAudioFocusLock, then mRCStack 1752 * Check whether the remote control display should be updated, triggers the update if required 1753 * @param infoChangedFlags the flags corresponding to the remote control client information 1754 * that has changed, if applicable (checking for the update conditions might trigger a 1755 * clear, rather than an update event). 1756 */ 1757 private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) { 1758 // determine whether the remote control display should be refreshed 1759 // if either stack is empty, there is a mismatch, so clear the RC display 1760 if (mRCStack.isEmpty() || mFocusStack.isEmpty()) { 1761 clearRemoteControlDisplay_syncAfRcs(); 1762 return; 1763 } 1764 1765 // determine which entry in the AudioFocus stack to consider, and compare against the 1766 // top of the stack for the media button event receivers : simply using the top of the 1767 // stack would make the entry disappear from the RemoteControlDisplay in conditions such as 1768 // notifications playing during music playback. 1769 // Crawl the AudioFocus stack from the top until an entry is found with the following 1770 // characteristics: 1771 // - focus gain on STREAM_MUSIC stream 1772 // - non-transient focus gain on a stream other than music 1773 FocusRequester af = null; 1774 try { 1775 for (int index = mFocusStack.size()-1; index >= 0; index--) { 1776 FocusRequester fr = mFocusStack.elementAt(index); 1777 if ((fr.getStreamType() == AudioManager.STREAM_MUSIC) 1778 || (fr.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN)) { 1779 af = fr; 1780 break; 1781 } 1782 } 1783 } catch (ArrayIndexOutOfBoundsException e) { 1784 Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e); 1785 af = null; 1786 } 1787 if (af == null) { 1788 clearRemoteControlDisplay_syncAfRcs(); 1789 return; 1790 } 1791 1792 // if the audio focus and RC owners belong to different packages, there is a mismatch, clear 1793 if (!af.hasSamePackage(mRCStack.peek().mCallingPackageName)) { 1794 clearRemoteControlDisplay_syncAfRcs(); 1795 return; 1796 } 1797 // if the audio focus didn't originate from the same Uid as the one in which the remote 1798 // control information will be retrieved, clear 1799 if (!af.hasSameUid(mRCStack.peek().mCallingUid)) { 1800 clearRemoteControlDisplay_syncAfRcs(); 1801 return; 1802 } 1803 1804 // refresh conditions were verified: update the remote controls 1805 // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty 1806 updateRemoteControlDisplay_syncAfRcs(infoChangedFlags); 1807 } 1808 1809 /** 1810 * Helper function: 1811 * Post a message to asynchronously move the media button event receiver associated with the 1812 * given remote control client ID to the top of the remote control stack 1813 * @param rccId 1814 */ 1815 private void postPromoteRcc(int rccId) { 1816 sendMsg(mEventHandler, MSG_PROMOTE_RCC, SENDMSG_REPLACE, 1817 rccId /*arg1*/, 0, null, 0/*delay*/); 1818 } 1819 1820 private void onPromoteRcc(int rccId) { 1821 if (DEBUG_RC) { Log.d(TAG, "Promoting RCC " + rccId); } 1822 synchronized(mAudioFocusLock) { 1823 synchronized(mRCStack) { 1824 // ignore if given RCC ID is already at top of remote control stack 1825 if (!mRCStack.isEmpty() && (mRCStack.peek().mRccId == rccId)) { 1826 return; 1827 } 1828 int indexToPromote = -1; 1829 try { 1830 for (int index = mRCStack.size()-1; index >= 0; index--) { 1831 final RemoteControlStackEntry rcse = mRCStack.elementAt(index); 1832 if (rcse.mRccId == rccId) { 1833 indexToPromote = index; 1834 break; 1835 } 1836 } 1837 if (indexToPromote >= 0) { 1838 if (DEBUG_RC) { Log.d(TAG, " moving RCC from index " + indexToPromote 1839 + " to " + (mRCStack.size()-1)); } 1840 final RemoteControlStackEntry rcse = mRCStack.remove(indexToPromote); 1841 mRCStack.push(rcse); 1842 // the RC stack changed, reevaluate the display 1843 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 1844 } 1845 } catch (ArrayIndexOutOfBoundsException e) { 1846 // not expected to happen, indicates improper concurrent modification 1847 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e); 1848 } 1849 }//synchronized(mRCStack) 1850 }//synchronized(mAudioFocusLock) 1851 } 1852 1853 /** 1854 * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c) 1855 * precondition: mediaIntent != null 1856 */ 1857 protected void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver, 1858 IBinder token) { 1859 Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent); 1860 1861 synchronized(mAudioFocusLock) { 1862 synchronized(mRCStack) { 1863 if (pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token)) { 1864 // new RC client, assume every type of information shall be queried 1865 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 1866 } 1867 } 1868 } 1869 } 1870 1871 /** 1872 * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent) 1873 * precondition: mediaIntent != null, eventReceiver != null 1874 */ 1875 protected void unregisterMediaButtonIntent(PendingIntent mediaIntent) 1876 { 1877 Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent); 1878 1879 synchronized(mAudioFocusLock) { 1880 synchronized(mRCStack) { 1881 boolean topOfStackWillChange = isCurrentRcController(mediaIntent); 1882 removeMediaButtonReceiver_syncAfRcs(mediaIntent); 1883 if (topOfStackWillChange) { 1884 // current RC client will change, assume every type of info needs to be queried 1885 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 1886 } 1887 } 1888 } 1889 } 1890 1891 /** 1892 * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c) 1893 * precondition: c != null 1894 */ 1895 protected void registerMediaButtonEventReceiverForCalls(ComponentName c) { 1896 if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE") 1897 != PackageManager.PERMISSION_GRANTED) { 1898 Log.e(TAG, "Invalid permissions to register media button receiver for calls"); 1899 return; 1900 } 1901 synchronized(mRCStack) { 1902 mMediaReceiverForCalls = c; 1903 } 1904 } 1905 1906 /** 1907 * see AudioManager.unregisterMediaButtonEventReceiverForCalls() 1908 */ 1909 protected void unregisterMediaButtonEventReceiverForCalls() { 1910 if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE") 1911 != PackageManager.PERMISSION_GRANTED) { 1912 Log.e(TAG, "Invalid permissions to unregister media button receiver for calls"); 1913 return; 1914 } 1915 synchronized(mRCStack) { 1916 mMediaReceiverForCalls = null; 1917 } 1918 } 1919 1920 /** 1921 * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) 1922 * @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient 1923 * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient 1924 * without modifying the RC stack, but while still causing the display to refresh (will 1925 * become blank as a result of this) 1926 */ 1927 protected int registerRemoteControlClient(PendingIntent mediaIntent, 1928 IRemoteControlClient rcClient, String callingPackageName) { 1929 if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient); 1930 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED; 1931 synchronized(mAudioFocusLock) { 1932 synchronized(mRCStack) { 1933 boolean wasCurrentRcController = isCurrentRcController(mediaIntent); 1934 // store the new display information 1935 try { 1936 for (int index = mRCStack.size()-1; index >= 0; index--) { 1937 final RemoteControlStackEntry rcse = mRCStack.elementAt(index); 1938 if(rcse.mMediaIntent.equals(mediaIntent)) { 1939 // already had a remote control client? 1940 if (rcse.mRcClientDeathHandler != null) { 1941 // stop monitoring the old client's death 1942 rcse.unlinkToRcClientDeath(); 1943 } 1944 // save the new remote control client 1945 rcse.mRcClient = rcClient; 1946 rcse.mCallingPackageName = callingPackageName; 1947 rcse.mCallingUid = Binder.getCallingUid(); 1948 if (rcClient == null) { 1949 // here rcse.mRcClientDeathHandler is null; 1950 rcse.resetPlaybackInfo(); 1951 break; 1952 } 1953 rccId = rcse.mRccId; 1954 1955 // there is a new (non-null) client: 1956 // 1/ give the new client the displays (if any) 1957 if (mRcDisplays.size() > 0) { 1958 plugRemoteControlDisplaysIntoClient_syncRcStack(rcse.mRcClient); 1959 } 1960 // 2/ monitor the new client's death 1961 IBinder b = rcse.mRcClient.asBinder(); 1962 RcClientDeathHandler rcdh = 1963 new RcClientDeathHandler(b, rcse.mMediaIntent); 1964 try { 1965 b.linkToDeath(rcdh, 0); 1966 } catch (RemoteException e) { 1967 // remote control client is DOA, disqualify it 1968 Log.w(TAG, "registerRemoteControlClient() has a dead client " + b); 1969 rcse.mRcClient = null; 1970 } 1971 rcse.mRcClientDeathHandler = rcdh; 1972 break; 1973 } 1974 }//for 1975 } catch (ArrayIndexOutOfBoundsException e) { 1976 // not expected to happen, indicates improper concurrent modification 1977 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e); 1978 } 1979 1980 // if the eventReceiver is now at the top of the stack but wasn't before 1981 // then check for potential refresh of the remote controls 1982 if (isCurrentRcController(mediaIntent) && !wasCurrentRcController) { 1983 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 1984 } 1985 }//synchronized(mRCStack) 1986 }//synchronized(mAudioFocusLock) 1987 return rccId; 1988 } 1989 1990 /** 1991 * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...) 1992 * rcClient is guaranteed non-null 1993 */ 1994 protected void unregisterRemoteControlClient(PendingIntent mediaIntent, 1995 IRemoteControlClient rcClient) { 1996 if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient); 1997 synchronized(mAudioFocusLock) { 1998 synchronized(mRCStack) { 1999 boolean topRccChange = false; 2000 try { 2001 for (int index = mRCStack.size()-1; index >= 0; index--) { 2002 final RemoteControlStackEntry rcse = mRCStack.elementAt(index); 2003 if ((rcse.mMediaIntent.equals(mediaIntent)) 2004 && rcClient.equals(rcse.mRcClient)) { 2005 // we found the IRemoteControlClient to unregister 2006 // stop monitoring its death 2007 rcse.unlinkToRcClientDeath(); 2008 // reset the client-related fields 2009 rcse.mRcClient = null; 2010 rcse.mCallingPackageName = null; 2011 topRccChange = (index == mRCStack.size()-1); 2012 // there can only be one matching RCC in the RC stack, we're done 2013 break; 2014 } 2015 } 2016 } catch (ArrayIndexOutOfBoundsException e) { 2017 // not expected to happen, indicates improper concurrent modification 2018 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e); 2019 } 2020 if (topRccChange) { 2021 // no more RCC for the RCD, check for potential refresh of the remote controls 2022 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 2023 } 2024 } 2025 } 2026 } 2027 2028 2029 /** 2030 * A class to encapsulate all the information about a remote control display. 2031 * After instanciation, init() must always be called before the object is added in the list 2032 * of displays. 2033 * Before being removed from the list of displays, release() must always be called (otherwise 2034 * it will leak death handlers). 2035 */ 2036 private class DisplayInfoForServer implements IBinder.DeathRecipient { 2037 /** may never be null */ 2038 private final IRemoteControlDisplay mRcDisplay; 2039 private final IBinder mRcDisplayBinder; 2040 private int mArtworkExpectedWidth = -1; 2041 private int mArtworkExpectedHeight = -1; 2042 private boolean mWantsPositionSync = false; 2043 private ComponentName mClientNotifListComp; 2044 private boolean mEnabled = true; 2045 2046 public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) { 2047 if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h); 2048 mRcDisplay = rcd; 2049 mRcDisplayBinder = rcd.asBinder(); 2050 mArtworkExpectedWidth = w; 2051 mArtworkExpectedHeight = h; 2052 } 2053 2054 public boolean init() { 2055 try { 2056 mRcDisplayBinder.linkToDeath(this, 0); 2057 } catch (RemoteException e) { 2058 // remote control display is DOA, disqualify it 2059 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + mRcDisplayBinder); 2060 return false; 2061 } 2062 return true; 2063 } 2064 2065 public void release() { 2066 try { 2067 mRcDisplayBinder.unlinkToDeath(this, 0); 2068 } catch (java.util.NoSuchElementException e) { 2069 // not much we can do here, the display should have been unregistered anyway 2070 Log.e(TAG, "Error in DisplaInfoForServer.relase()", e); 2071 } 2072 } 2073 2074 public void binderDied() { 2075 synchronized(mRCStack) { 2076 Log.w(TAG, "RemoteControl: display " + mRcDisplay + " died"); 2077 // remove the display from the list 2078 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); 2079 while (displayIterator.hasNext()) { 2080 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); 2081 if (di.mRcDisplay == mRcDisplay) { 2082 if (DEBUG_RC) Log.w(TAG, " RCD removed from list"); 2083 displayIterator.remove(); 2084 return; 2085 } 2086 } 2087 } 2088 } 2089 } 2090 2091 /** 2092 * The remote control displays. 2093 * Access synchronized on mRCStack 2094 */ 2095 private ArrayList<DisplayInfoForServer> mRcDisplays = new ArrayList<DisplayInfoForServer>(1); 2096 2097 /** 2098 * Plug each registered display into the specified client 2099 * @param rcc, guaranteed non null 2100 */ 2101 private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) { 2102 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); 2103 while (displayIterator.hasNext()) { 2104 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); 2105 try { 2106 rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth, 2107 di.mArtworkExpectedHeight); 2108 if (di.mWantsPositionSync) { 2109 rcc.setWantsSyncForDisplay(di.mRcDisplay, true); 2110 } 2111 } catch (RemoteException e) { 2112 Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e); 2113 } 2114 } 2115 } 2116 2117 private void enableRemoteControlDisplayForClient_syncRcStack(IRemoteControlDisplay rcd, 2118 boolean enabled) { 2119 // let all the remote control clients know whether the given display is enabled 2120 // (so the remote control stack traversal order doesn't matter). 2121 final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2122 while(stackIterator.hasNext()) { 2123 RemoteControlStackEntry rcse = stackIterator.next(); 2124 if(rcse.mRcClient != null) { 2125 try { 2126 rcse.mRcClient.enableRemoteControlDisplay(rcd, enabled); 2127 } catch (RemoteException e) { 2128 Log.e(TAG, "Error connecting RCD to client: ", e); 2129 } 2130 } 2131 } 2132 } 2133 2134 /** 2135 * Is the remote control display interface already registered 2136 * @param rcd 2137 * @return true if the IRemoteControlDisplay is already in the list of displays 2138 */ 2139 private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) { 2140 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); 2141 while (displayIterator.hasNext()) { 2142 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); 2143 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { 2144 return true; 2145 } 2146 } 2147 return false; 2148 } 2149 2150 /** 2151 * Register an IRemoteControlDisplay. 2152 * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient 2153 * at the top of the stack to update the new display with its information. 2154 * @see android.media.IAudioService#registerRemoteControlDisplay(android.media.IRemoteControlDisplay, int, int) 2155 * @param rcd the IRemoteControlDisplay to register. No effect if null. 2156 * @param w the maximum width of the expected bitmap. Negative or zero values indicate this 2157 * display doesn't need to receive artwork. 2158 * @param h the maximum height of the expected bitmap. Negative or zero values indicate this 2159 * display doesn't need to receive artwork. 2160 * @param listenerComp the component for the listener interface, may be null if it's not needed 2161 * to verify it belongs to one of the enabled notification listeners 2162 */ 2163 private void registerRemoteControlDisplay_int(IRemoteControlDisplay rcd, int w, int h, 2164 ComponentName listenerComp) { 2165 if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")"); 2166 synchronized(mAudioFocusLock) { 2167 synchronized(mRCStack) { 2168 if ((rcd == null) || rcDisplayIsPluggedIn_syncRcStack(rcd)) { 2169 return; 2170 } 2171 DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h); 2172 di.mEnabled = true; 2173 di.mClientNotifListComp = listenerComp; 2174 if (!di.init()) { 2175 if (DEBUG_RC) Log.e(TAG, " error registering RCD"); 2176 return; 2177 } 2178 // add RCD to list of displays 2179 mRcDisplays.add(di); 2180 2181 // let all the remote control clients know there is a new display (so the remote 2182 // control stack traversal order doesn't matter). 2183 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2184 while(stackIterator.hasNext()) { 2185 RemoteControlStackEntry rcse = stackIterator.next(); 2186 if(rcse.mRcClient != null) { 2187 try { 2188 rcse.mRcClient.plugRemoteControlDisplay(rcd, w, h); 2189 } catch (RemoteException e) { 2190 Log.e(TAG, "Error connecting RCD to client: ", e); 2191 } 2192 } 2193 } 2194 2195 // we have a new display, of which all the clients are now aware: have it be 2196 // initialized wih the current gen ID and the current client info, do not 2197 // reset the information for the other (existing) displays 2198 sendMsg(mEventHandler, MSG_RCDISPLAY_INIT_INFO, SENDMSG_QUEUE, 2199 w /*arg1*/, h /*arg2*/, 2200 rcd /*obj*/, 0/*delay*/); 2201 } 2202 } 2203 } 2204 2205 /** 2206 * Unregister an IRemoteControlDisplay. 2207 * No effect if the IRemoteControlDisplay hasn't been successfully registered. 2208 * @see android.media.IAudioService#unregisterRemoteControlDisplay(android.media.IRemoteControlDisplay) 2209 * @param rcd the IRemoteControlDisplay to unregister. No effect if null. 2210 */ 2211 protected void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) { 2212 if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")"); 2213 synchronized(mRCStack) { 2214 if (rcd == null) { 2215 return; 2216 } 2217 2218 boolean displayWasPluggedIn = false; 2219 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); 2220 while (displayIterator.hasNext() && !displayWasPluggedIn) { 2221 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); 2222 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { 2223 displayWasPluggedIn = true; 2224 di.release(); 2225 displayIterator.remove(); 2226 } 2227 } 2228 2229 if (displayWasPluggedIn) { 2230 // disconnect this remote control display from all the clients, so the remote 2231 // control stack traversal order doesn't matter 2232 final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2233 while(stackIterator.hasNext()) { 2234 final RemoteControlStackEntry rcse = stackIterator.next(); 2235 if(rcse.mRcClient != null) { 2236 try { 2237 rcse.mRcClient.unplugRemoteControlDisplay(rcd); 2238 } catch (RemoteException e) { 2239 Log.e(TAG, "Error disconnecting remote control display to client: ", e); 2240 } 2241 } 2242 } 2243 } else { 2244 if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD"); 2245 } 2246 } 2247 } 2248 2249 /** 2250 * Update the size of the artwork used by an IRemoteControlDisplay. 2251 * @see android.media.IAudioService#remoteControlDisplayUsesBitmapSize(android.media.IRemoteControlDisplay, int, int) 2252 * @param rcd the IRemoteControlDisplay with the new artwork size requirement 2253 * @param w the maximum width of the expected bitmap. Negative or zero values indicate this 2254 * display doesn't need to receive artwork. 2255 * @param h the maximum height of the expected bitmap. Negative or zero values indicate this 2256 * display doesn't need to receive artwork. 2257 */ 2258 protected void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) { 2259 synchronized(mRCStack) { 2260 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); 2261 boolean artworkSizeUpdate = false; 2262 while (displayIterator.hasNext() && !artworkSizeUpdate) { 2263 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); 2264 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { 2265 if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) { 2266 di.mArtworkExpectedWidth = w; 2267 di.mArtworkExpectedHeight = h; 2268 artworkSizeUpdate = true; 2269 } 2270 } 2271 } 2272 if (artworkSizeUpdate) { 2273 // RCD is currently plugged in and its artwork size has changed, notify all RCCs, 2274 // stack traversal order doesn't matter 2275 final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2276 while(stackIterator.hasNext()) { 2277 final RemoteControlStackEntry rcse = stackIterator.next(); 2278 if(rcse.mRcClient != null) { 2279 try { 2280 rcse.mRcClient.setBitmapSizeForDisplay(rcd, w, h); 2281 } catch (RemoteException e) { 2282 Log.e(TAG, "Error setting bitmap size for RCD on RCC: ", e); 2283 } 2284 } 2285 } 2286 } 2287 } 2288 } 2289 2290 /** 2291 * Controls whether a remote control display needs periodic checks of the RemoteControlClient 2292 * playback position to verify that the estimated position has not drifted from the actual 2293 * position. By default the check is not performed. 2294 * The IRemoteControlDisplay must have been previously registered for this to have any effect. 2295 * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled 2296 * or disabled. Not null. 2297 * @param wantsSync if true, RemoteControlClient instances which expose their playback position 2298 * to the framework will regularly compare the estimated playback position with the actual 2299 * position, and will update the IRemoteControlDisplay implementation whenever a drift is 2300 * detected. 2301 */ 2302 protected void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd, 2303 boolean wantsSync) { 2304 synchronized(mRCStack) { 2305 boolean rcdRegistered = false; 2306 // store the information about this display 2307 // (display stack traversal order doesn't matter). 2308 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator(); 2309 while (displayIterator.hasNext()) { 2310 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next(); 2311 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) { 2312 di.mWantsPositionSync = wantsSync; 2313 rcdRegistered = true; 2314 break; 2315 } 2316 } 2317 if (!rcdRegistered) { 2318 return; 2319 } 2320 // notify all current RemoteControlClients 2321 // (stack traversal order doesn't matter as we notify all RCCs) 2322 final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2323 while (stackIterator.hasNext()) { 2324 final RemoteControlStackEntry rcse = stackIterator.next(); 2325 if (rcse.mRcClient != null) { 2326 try { 2327 rcse.mRcClient.setWantsSyncForDisplay(rcd, wantsSync); 2328 } catch (RemoteException e) { 2329 Log.e(TAG, "Error setting position sync flag for RCD on RCC: ", e); 2330 } 2331 } 2332 } 2333 } 2334 } 2335 2336 protected void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) { 2337 // ignore position change requests if invalid generation ID 2338 synchronized(mRCStack) { 2339 synchronized(mCurrentRcLock) { 2340 if (mCurrentRcClientGen != generationId) { 2341 return; 2342 } 2343 } 2344 } 2345 // discard any unprocessed seek request in the message queue, and replace with latest 2346 sendMsg(mEventHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */, 2347 0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */); 2348 } 2349 2350 private void onSetRemoteControlClientPlaybackPosition(int generationId, long timeMs) { 2351 if(DEBUG_RC) Log.d(TAG, "onSetRemoteControlClientPlaybackPosition(genId=" + generationId + 2352 ", timeMs=" + timeMs + ")"); 2353 synchronized(mRCStack) { 2354 synchronized(mCurrentRcLock) { 2355 if ((mCurrentRcClient != null) && (mCurrentRcClientGen == generationId)) { 2356 // tell the current client to seek to the requested location 2357 try { 2358 mCurrentRcClient.seekTo(generationId, timeMs); 2359 } catch (RemoteException e) { 2360 Log.e(TAG, "Current valid remote client is dead: "+e); 2361 mCurrentRcClient = null; 2362 } 2363 } 2364 } 2365 } 2366 } 2367 2368 protected void updateRemoteControlClientMetadata(int genId, int key, Rating value) { 2369 sendMsg(mEventHandler, MSG_RCC_UPDATE_METADATA, SENDMSG_QUEUE, 2370 genId /* arg1 */, key /* arg2 */, value /* obj */, 0 /* delay */); 2371 } 2372 2373 private void onUpdateRemoteControlClientMetadata(int genId, int key, Rating value) { 2374 if(DEBUG_RC) Log.d(TAG, "onUpdateRemoteControlClientMetadata(genId=" + genId + 2375 ", what=" + key + ",rating=" + value + ")"); 2376 synchronized(mRCStack) { 2377 synchronized(mCurrentRcLock) { 2378 if ((mCurrentRcClient != null) && (mCurrentRcClientGen == genId)) { 2379 try { 2380 switch (key) { 2381 case MediaMetadataEditor.RATING_KEY_BY_USER: 2382 mCurrentRcClient.updateMetadata(genId, key, value); 2383 break; 2384 default: 2385 Log.e(TAG, "unhandled metadata key " + key + " update for RCC " 2386 + genId); 2387 break; 2388 } 2389 } catch (RemoteException e) { 2390 Log.e(TAG, "Current valid remote client is dead", e); 2391 mCurrentRcClient = null; 2392 } 2393 } 2394 } 2395 } 2396 } 2397 2398 protected void setPlaybackInfoForRcc(int rccId, int what, int value) { 2399 sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE, 2400 rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */); 2401 } 2402 2403 // handler for MSG_RCC_NEW_PLAYBACK_INFO 2404 private void onNewPlaybackInfoForRcc(int rccId, int key, int value) { 2405 if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId + 2406 ", what=" + key + ",val=" + value + ")"); 2407 synchronized(mRCStack) { 2408 // iterating from top of stack as playback information changes are more likely 2409 // on entries at the top of the remote control stack 2410 try { 2411 for (int index = mRCStack.size()-1; index >= 0; index--) { 2412 final RemoteControlStackEntry rcse = mRCStack.elementAt(index); 2413 if (rcse.mRccId == rccId) { 2414 switch (key) { 2415 case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE: 2416 rcse.mPlaybackType = value; 2417 postReevaluateRemote(); 2418 break; 2419 case RemoteControlClient.PLAYBACKINFO_VOLUME: 2420 rcse.mPlaybackVolume = value; 2421 synchronized (mMainRemote) { 2422 if (rccId == mMainRemote.mRccId) { 2423 mMainRemote.mVolume = value; 2424 mVolumeController.postHasNewRemotePlaybackInfo(); 2425 } 2426 } 2427 break; 2428 case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX: 2429 rcse.mPlaybackVolumeMax = value; 2430 synchronized (mMainRemote) { 2431 if (rccId == mMainRemote.mRccId) { 2432 mMainRemote.mVolumeMax = value; 2433 mVolumeController.postHasNewRemotePlaybackInfo(); 2434 } 2435 } 2436 break; 2437 case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING: 2438 rcse.mPlaybackVolumeHandling = value; 2439 synchronized (mMainRemote) { 2440 if (rccId == mMainRemote.mRccId) { 2441 mMainRemote.mVolumeHandling = value; 2442 mVolumeController.postHasNewRemotePlaybackInfo(); 2443 } 2444 } 2445 break; 2446 case RemoteControlClient.PLAYBACKINFO_USES_STREAM: 2447 rcse.mPlaybackStream = value; 2448 break; 2449 default: 2450 Log.e(TAG, "unhandled key " + key + " for RCC " + rccId); 2451 break; 2452 } 2453 return; 2454 } 2455 }//for 2456 } catch (ArrayIndexOutOfBoundsException e) { 2457 // not expected to happen, indicates improper concurrent modification 2458 Log.e(TAG, "Wrong index mRCStack on onNewPlaybackInfoForRcc, lock error? ", e); 2459 } 2460 } 2461 } 2462 2463 protected void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) { 2464 sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE, 2465 rccId /* arg1 */, state /* arg2 */, 2466 new RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */); 2467 } 2468 2469 private void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) { 2470 if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state 2471 + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")"); 2472 synchronized(mRCStack) { 2473 // iterating from top of stack as playback information changes are more likely 2474 // on entries at the top of the remote control stack 2475 try { 2476 for (int index = mRCStack.size()-1; index >= 0; index--) { 2477 final RemoteControlStackEntry rcse = mRCStack.elementAt(index); 2478 if (rcse.mRccId == rccId) { 2479 rcse.mPlaybackState = newState; 2480 synchronized (mMainRemote) { 2481 if (rccId == mMainRemote.mRccId) { 2482 mMainRemoteIsActive = isPlaystateActive(state); 2483 postReevaluateRemote(); 2484 } 2485 } 2486 // an RCC moving to a "playing" state should become the media button 2487 // event receiver so it can be controlled, without requiring the 2488 // app to re-register its receiver 2489 if (isPlaystateActive(state)) { 2490 postPromoteRcc(rccId); 2491 } 2492 } 2493 }//for 2494 } catch (ArrayIndexOutOfBoundsException e) { 2495 // not expected to happen, indicates improper concurrent modification 2496 Log.e(TAG, "Wrong index on mRCStack in onNewPlaybackStateForRcc, lock error? ", e); 2497 } 2498 } 2499 } 2500 2501 protected void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) { 2502 sendMsg(mEventHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE, 2503 rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */); 2504 } 2505 2506 // handler for MSG_RCC_NEW_VOLUME_OBS 2507 private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) { 2508 synchronized(mRCStack) { 2509 // The stack traversal order doesn't matter because there is only one stack entry 2510 // with this RCC ID, but the matching ID is more likely at the top of the stack, so 2511 // start iterating from the top. 2512 try { 2513 for (int index = mRCStack.size()-1; index >= 0; index--) { 2514 final RemoteControlStackEntry rcse = mRCStack.elementAt(index); 2515 if (rcse.mRccId == rccId) { 2516 rcse.mRemoteVolumeObs = rvo; 2517 break; 2518 } 2519 } 2520 } catch (ArrayIndexOutOfBoundsException e) { 2521 // not expected to happen, indicates improper concurrent modification 2522 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e); 2523 } 2524 } 2525 } 2526 2527 /** 2528 * Checks if a remote client is active on the supplied stream type. Update the remote stream 2529 * volume state if found and playing 2530 * @param streamType 2531 * @return false if no remote playing is currently playing 2532 */ 2533 protected boolean checkUpdateRemoteStateIfActive(int streamType) { 2534 synchronized(mRCStack) { 2535 // iterating from top of stack as active playback is more likely on entries at the top 2536 try { 2537 for (int index = mRCStack.size()-1; index >= 0; index--) { 2538 final RemoteControlStackEntry rcse = mRCStack.elementAt(index); 2539 if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) 2540 && isPlaystateActive(rcse.mPlaybackState.mState) 2541 && (rcse.mPlaybackStream == streamType)) { 2542 if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType 2543 + ", vol =" + rcse.mPlaybackVolume); 2544 synchronized (mMainRemote) { 2545 mMainRemote.mRccId = rcse.mRccId; 2546 mMainRemote.mVolume = rcse.mPlaybackVolume; 2547 mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax; 2548 mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling; 2549 mMainRemoteIsActive = true; 2550 } 2551 return true; 2552 } 2553 } 2554 } catch (ArrayIndexOutOfBoundsException e) { 2555 // not expected to happen, indicates improper concurrent modification 2556 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e); 2557 } 2558 } 2559 synchronized (mMainRemote) { 2560 mMainRemoteIsActive = false; 2561 } 2562 return false; 2563 } 2564 2565 /** 2566 * Returns true if the given playback state is considered "active", i.e. it describes a state 2567 * where playback is happening, or about to 2568 * @param playState the playback state to evaluate 2569 * @return true if active, false otherwise (inactive or unknown) 2570 */ 2571 private static boolean isPlaystateActive(int playState) { 2572 switch (playState) { 2573 case RemoteControlClient.PLAYSTATE_PLAYING: 2574 case RemoteControlClient.PLAYSTATE_BUFFERING: 2575 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: 2576 case RemoteControlClient.PLAYSTATE_REWINDING: 2577 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: 2578 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: 2579 return true; 2580 default: 2581 return false; 2582 } 2583 } 2584 2585 protected void adjustRemoteVolume(int streamType, int direction, int flags) { 2586 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED; 2587 boolean volFixed = false; 2588 synchronized (mMainRemote) { 2589 if (!mMainRemoteIsActive) { 2590 if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client"); 2591 return; 2592 } 2593 rccId = mMainRemote.mRccId; 2594 volFixed = (mMainRemote.mVolumeHandling == 2595 RemoteControlClient.PLAYBACK_VOLUME_FIXED); 2596 } 2597 // unlike "local" stream volumes, we can't compute the new volume based on the direction, 2598 // we can only notify the remote that volume needs to be updated, and we'll get an async' 2599 // update through setPlaybackInfoForRcc() 2600 if (!volFixed) { 2601 sendVolumeUpdateToRemote(rccId, direction); 2602 } 2603 2604 // fire up the UI 2605 mVolumeController.postRemoteVolumeChanged(streamType, flags); 2606 } 2607 2608 private void sendVolumeUpdateToRemote(int rccId, int direction) { 2609 if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); } 2610 if (direction == 0) { 2611 // only handling discrete events 2612 return; 2613 } 2614 IRemoteVolumeObserver rvo = null; 2615 synchronized (mRCStack) { 2616 // The stack traversal order doesn't matter because there is only one stack entry 2617 // with this RCC ID, but the matching ID is more likely at the top of the stack, so 2618 // start iterating from the top. 2619 try { 2620 for (int index = mRCStack.size()-1; index >= 0; index--) { 2621 final RemoteControlStackEntry rcse = mRCStack.elementAt(index); 2622 //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate? 2623 if (rcse.mRccId == rccId) { 2624 rvo = rcse.mRemoteVolumeObs; 2625 break; 2626 } 2627 } 2628 } catch (ArrayIndexOutOfBoundsException e) { 2629 // not expected to happen, indicates improper concurrent modification 2630 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e); 2631 } 2632 } 2633 if (rvo != null) { 2634 try { 2635 rvo.dispatchRemoteVolumeUpdate(direction, -1); 2636 } catch (RemoteException e) { 2637 Log.e(TAG, "Error dispatching relative volume update", e); 2638 } 2639 } 2640 } 2641 2642 protected int getRemoteStreamMaxVolume() { 2643 synchronized (mMainRemote) { 2644 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) { 2645 return 0; 2646 } 2647 return mMainRemote.mVolumeMax; 2648 } 2649 } 2650 2651 protected int getRemoteStreamVolume() { 2652 synchronized (mMainRemote) { 2653 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) { 2654 return 0; 2655 } 2656 return mMainRemote.mVolume; 2657 } 2658 } 2659 2660 protected void setRemoteStreamVolume(int vol) { 2661 if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); } 2662 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED; 2663 synchronized (mMainRemote) { 2664 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) { 2665 return; 2666 } 2667 rccId = mMainRemote.mRccId; 2668 } 2669 IRemoteVolumeObserver rvo = null; 2670 synchronized (mRCStack) { 2671 // The stack traversal order doesn't matter because there is only one stack entry 2672 // with this RCC ID, but the matching ID is more likely at the top of the stack, so 2673 // start iterating from the top. 2674 try { 2675 for (int index = mRCStack.size()-1; index >= 0; index--) { 2676 final RemoteControlStackEntry rcse = mRCStack.elementAt(index); 2677 //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate? 2678 if (rcse.mRccId == rccId) { 2679 rvo = rcse.mRemoteVolumeObs; 2680 break; 2681 } 2682 } 2683 } catch (ArrayIndexOutOfBoundsException e) { 2684 // not expected to happen, indicates improper concurrent modification 2685 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e); 2686 } 2687 } 2688 if (rvo != null) { 2689 try { 2690 rvo.dispatchRemoteVolumeUpdate(0, vol); 2691 } catch (RemoteException e) { 2692 Log.e(TAG, "Error dispatching absolute volume update", e); 2693 } 2694 } 2695 } 2696 2697 /** 2698 * Call to make AudioService reevaluate whether it's in a mode where remote players should 2699 * have their volume controlled. In this implementation this is only to reset whether 2700 * VolumePanel should display remote volumes 2701 */ 2702 private void postReevaluateRemote() { 2703 sendMsg(mEventHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0); 2704 } 2705 2706 private void onReevaluateRemote() { 2707 if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); } 2708 // is there a registered RemoteControlClient that is handling remote playback 2709 boolean hasRemotePlayback = false; 2710 synchronized (mRCStack) { 2711 // iteration stops when PLAYBACK_TYPE_REMOTE is found, so remote control stack 2712 // traversal order doesn't matter 2713 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2714 while(stackIterator.hasNext()) { 2715 RemoteControlStackEntry rcse = stackIterator.next(); 2716 if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) { 2717 hasRemotePlayback = true; 2718 break; 2719 } 2720 } 2721 } 2722 synchronized (mMainRemote) { 2723 if (mHasRemotePlayback != hasRemotePlayback) { 2724 mHasRemotePlayback = hasRemotePlayback; 2725 mVolumeController.postRemoteSliderVisibility(hasRemotePlayback); 2726 } 2727 } 2728 } 2729 2730} 2731