Home | History | Annotate | Download | only in media
      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 
     17 package android.media;
     18 
     19 import android.app.Activity;
     20 import android.app.ActivityManager;
     21 import android.app.AppOpsManager;
     22 import android.app.KeyguardManager;
     23 import android.app.PendingIntent;
     24 import android.app.PendingIntent.CanceledException;
     25 import android.app.PendingIntent.OnFinished;
     26 import android.content.ActivityNotFoundException;
     27 import android.content.BroadcastReceiver;
     28 import android.content.ComponentName;
     29 import android.content.ContentResolver;
     30 import android.content.Context;
     31 import android.content.Intent;
     32 import android.content.IntentFilter;
     33 import android.content.pm.PackageManager;
     34 import android.database.ContentObserver;
     35 import android.media.PlayerRecord.RemotePlaybackState;
     36 import android.net.Uri;
     37 import android.os.Binder;
     38 import android.os.Bundle;
     39 import android.os.Handler;
     40 import android.os.IBinder;
     41 import android.os.Looper;
     42 import android.os.Message;
     43 import android.os.PowerManager;
     44 import android.os.RemoteException;
     45 import android.os.UserHandle;
     46 import android.os.IBinder.DeathRecipient;
     47 import android.provider.Settings;
     48 import android.speech.RecognizerIntent;
     49 import android.telephony.PhoneStateListener;
     50 import android.telephony.TelephonyManager;
     51 import android.util.Log;
     52 import android.util.Slog;
     53 import android.view.KeyEvent;
     54 
     55 import java.io.PrintWriter;
     56 import java.util.ArrayList;
     57 import java.util.Iterator;
     58 import java.util.Stack;
     59 
     60 /**
     61  * @hide
     62  *
     63  */
     64 public 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 AudioService.VolumeController mVolumeController;
     81     private final AppOpsManager mAppOps;
     82     private final KeyguardManager mKeyguardManager;
     83     private final AudioService mAudioService;
     84     private final NotificationListenerObserver mNotifListenerObserver;
     85 
     86     protected MediaFocusControl(Looper looper, Context cntxt,
     87             AudioService.VolumeController volumeCtrl, AudioService as) {
     88         mEventHandler = new MediaEventHandler(looper);
     89         mContext = cntxt;
     90         mContentResolver = mContext.getContentResolver();
     91         mVolumeController = volumeCtrl;
     92         mAudioService = as;
     93 
     94         PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
     95         mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
     96         mMainRemote = new RemotePlaybackState(-1,
     97                 AudioService.getMaxStreamVolume(AudioManager.STREAM_MUSIC),
     98                 AudioService.getMaxStreamVolume(AudioManager.STREAM_MUSIC));
     99 
    100         // Register for phone state monitoring
    101         TelephonyManager tmgr = (TelephonyManager)
    102                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
    103         tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    104 
    105         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
    106         mKeyguardManager =
    107                 (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
    108         mNotifListenerObserver = new NotificationListenerObserver();
    109 
    110         mHasRemotePlayback = false;
    111         mMainRemoteIsActive = false;
    112 
    113         PlayerRecord.setMediaFocusControl(this);
    114 
    115         postReevaluateRemote();
    116     }
    117 
    118     protected void dump(PrintWriter pw) {
    119         dumpFocusStack(pw);
    120         dumpRCStack(pw);
    121         dumpRCCStack(pw);
    122         dumpRCDList(pw);
    123     }
    124 
    125     //==========================================================================================
    126     // Management of RemoteControlDisplay registration permissions
    127     //==========================================================================================
    128     private final static Uri ENABLED_NOTIFICATION_LISTENERS_URI =
    129             Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
    130 
    131     private class NotificationListenerObserver extends ContentObserver {
    132 
    133         NotificationListenerObserver() {
    134             super(mEventHandler);
    135             mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
    136                     Settings.Secure.ENABLED_NOTIFICATION_LISTENERS), false, this);
    137         }
    138 
    139         @Override
    140         public void onChange(boolean selfChange, Uri uri) {
    141             if (!ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri) || selfChange) {
    142                 return;
    143             }
    144             if (DEBUG_RC) { Log.d(TAG, "NotificationListenerObserver.onChange()"); }
    145             postReevaluateRemoteControlDisplays();
    146         }
    147     }
    148 
    149     private final static int RCD_REG_FAILURE = 0;
    150     private final static int RCD_REG_SUCCESS_PERMISSION = 1;
    151     private final static int RCD_REG_SUCCESS_ENABLED_NOTIF = 2;
    152 
    153     /**
    154      * Checks a caller's authorization to register an IRemoteControlDisplay.
    155      * Authorization is granted if one of the following is true:
    156      * <ul>
    157      * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL permission</li>
    158      * <li>the caller's listener is one of the enabled notification listeners</li>
    159      * </ul>
    160      * @return RCD_REG_FAILURE if it's not safe to proceed with the IRemoteControlDisplay
    161      *     registration.
    162      */
    163     private int checkRcdRegistrationAuthorization(ComponentName listenerComp) {
    164         // MEDIA_CONTENT_CONTROL permission check
    165         if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
    166                 android.Manifest.permission.MEDIA_CONTENT_CONTROL)) {
    167             if (DEBUG_RC) { Log.d(TAG, "ok to register Rcd: has MEDIA_CONTENT_CONTROL permission");}
    168             return RCD_REG_SUCCESS_PERMISSION;
    169         }
    170 
    171         // ENABLED_NOTIFICATION_LISTENERS settings check
    172         if (listenerComp != null) {
    173             // this call is coming from an app, can't use its identity to read secure settings
    174             final long ident = Binder.clearCallingIdentity();
    175             try {
    176                 final int currentUser = ActivityManager.getCurrentUser();
    177                 final String enabledNotifListeners = Settings.Secure.getStringForUser(
    178                         mContext.getContentResolver(),
    179                         Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
    180                         currentUser);
    181                 if (enabledNotifListeners != null) {
    182                     final String[] components = enabledNotifListeners.split(":");
    183                     for (int i=0; i<components.length; i++) {
    184                         final ComponentName component =
    185                                 ComponentName.unflattenFromString(components[i]);
    186                         if (component != null) {
    187                             if (listenerComp.equals(component)) {
    188                                 if (DEBUG_RC) { Log.d(TAG, "ok to register RCC: " + component +
    189                                         " is authorized notification listener"); }
    190                                 return RCD_REG_SUCCESS_ENABLED_NOTIF;
    191                             }
    192                         }
    193                     }
    194                 }
    195                 if (DEBUG_RC) { Log.d(TAG, "not ok to register RCD, " + listenerComp +
    196                         " is not in list of ENABLED_NOTIFICATION_LISTENERS"); }
    197             } finally {
    198                 Binder.restoreCallingIdentity(ident);
    199             }
    200         }
    201 
    202         return RCD_REG_FAILURE;
    203     }
    204 
    205     protected boolean registerRemoteController(IRemoteControlDisplay rcd, int w, int h,
    206             ComponentName listenerComp) {
    207         int reg = checkRcdRegistrationAuthorization(listenerComp);
    208         if (reg != RCD_REG_FAILURE) {
    209             registerRemoteControlDisplay_int(rcd, w, h, listenerComp);
    210             return true;
    211         } else {
    212             Slog.w(TAG, "Access denied to process: " + Binder.getCallingPid() +
    213                     ", must have permission " + android.Manifest.permission.MEDIA_CONTENT_CONTROL +
    214                     " or be an enabled NotificationListenerService for registerRemoteController");
    215             return false;
    216         }
    217     }
    218 
    219     protected boolean registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
    220         int reg = checkRcdRegistrationAuthorization(null);
    221         if (reg != RCD_REG_FAILURE) {
    222             registerRemoteControlDisplay_int(rcd, w, h, null);
    223             return true;
    224         } else {
    225             Slog.w(TAG, "Access denied to process: " + Binder.getCallingPid() +
    226                     ", must have permission " + android.Manifest.permission.MEDIA_CONTENT_CONTROL +
    227                     " to register IRemoteControlDisplay");
    228             return false;
    229         }
    230     }
    231 
    232     private void postReevaluateRemoteControlDisplays() {
    233         sendMsg(mEventHandler, MSG_REEVALUATE_RCD, SENDMSG_QUEUE, 0, 0, null, 0);
    234     }
    235 
    236     private void onReevaluateRemoteControlDisplays() {
    237         if (DEBUG_RC) { Log.d(TAG, "onReevaluateRemoteControlDisplays()"); }
    238         // read which components are enabled notification listeners
    239         final int currentUser = ActivityManager.getCurrentUser();
    240         final String enabledNotifListeners = Settings.Secure.getStringForUser(
    241                 mContext.getContentResolver(),
    242                 Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
    243                 currentUser);
    244         if (DEBUG_RC) { Log.d(TAG, " > enabled list: " + enabledNotifListeners); }
    245         synchronized(mAudioFocusLock) {
    246             synchronized(mPRStack) {
    247                 // check whether the "enable" status of each RCD with a notification listener
    248                 // has changed
    249                 final String[] enabledComponents;
    250                 if (enabledNotifListeners == null) {
    251                     enabledComponents = null;
    252                 } else {
    253                     enabledComponents = enabledNotifListeners.split(":");
    254                 }
    255                 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
    256                 while (displayIterator.hasNext()) {
    257                     final DisplayInfoForServer di =
    258                             displayIterator.next();
    259                     if (di.mClientNotifListComp != null) {
    260                         boolean wasEnabled = di.mEnabled;
    261                         di.mEnabled = isComponentInStringArray(di.mClientNotifListComp,
    262                                 enabledComponents);
    263                         if (wasEnabled != di.mEnabled){
    264                             try {
    265                                 // tell the RCD whether it's enabled
    266                                 di.mRcDisplay.setEnabled(di.mEnabled);
    267                                 // tell the RCCs about the change for this RCD
    268                                 enableRemoteControlDisplayForClient_syncRcStack(
    269                                         di.mRcDisplay, di.mEnabled);
    270                                 // when enabling, refresh the information on the display
    271                                 if (di.mEnabled) {
    272                                     sendMsg(mEventHandler, MSG_RCDISPLAY_INIT_INFO, SENDMSG_QUEUE,
    273                                             di.mArtworkExpectedWidth /*arg1*/,
    274                                             di.mArtworkExpectedHeight/*arg2*/,
    275                                             di.mRcDisplay /*obj*/, 0/*delay*/);
    276                                 }
    277                             } catch (RemoteException e) {
    278                                 Log.e(TAG, "Error en/disabling RCD: ", e);
    279                             }
    280                         }
    281                     }
    282                 }
    283             }
    284         }
    285     }
    286 
    287     /**
    288      * @param comp a non-null ComponentName
    289      * @param enabledArray may be null
    290      * @return
    291      */
    292     private boolean isComponentInStringArray(ComponentName comp, String[] enabledArray) {
    293         if (enabledArray == null || enabledArray.length == 0) {
    294             if (DEBUG_RC) { Log.d(TAG, " > " + comp + " is NOT enabled"); }
    295             return false;
    296         }
    297         final String compString = comp.flattenToString();
    298         for (int i=0; i<enabledArray.length; i++) {
    299             if (compString.equals(enabledArray[i])) {
    300                 if (DEBUG_RC) { Log.d(TAG, " > " + compString + " is enabled"); }
    301                 return true;
    302             }
    303         }
    304         if (DEBUG_RC) { Log.d(TAG, " > " + compString + " is NOT enabled"); }
    305         return false;
    306     }
    307 
    308     //==========================================================================================
    309     // Internal event handling
    310     //==========================================================================================
    311 
    312     // event handler messages
    313     private static final int MSG_RCDISPLAY_CLEAR = 1;
    314     private static final int MSG_RCDISPLAY_UPDATE = 2;
    315     private static final int MSG_REEVALUATE_REMOTE = 3;
    316     private static final int MSG_RCC_NEW_PLAYBACK_INFO = 4;
    317     private static final int MSG_RCC_NEW_VOLUME_OBS = 5;
    318     private static final int MSG_RCC_NEW_PLAYBACK_STATE = 6;
    319     private static final int MSG_RCC_SEEK_REQUEST = 7;
    320     private static final int MSG_RCC_UPDATE_METADATA = 8;
    321     private static final int MSG_RCDISPLAY_INIT_INFO = 9;
    322     private static final int MSG_REEVALUATE_RCD = 10;
    323     private static final int MSG_UNREGISTER_MEDIABUTTONINTENT = 11;
    324 
    325     // sendMsg() flags
    326     /** If the msg is already queued, replace it with this one. */
    327     private static final int SENDMSG_REPLACE = 0;
    328     /** If the msg is already queued, ignore this one and leave the old. */
    329     private static final int SENDMSG_NOOP = 1;
    330     /** If the msg is already queued, queue this one and leave the old. */
    331     private static final int SENDMSG_QUEUE = 2;
    332 
    333     private static void sendMsg(Handler handler, int msg,
    334             int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
    335 
    336         if (existingMsgPolicy == SENDMSG_REPLACE) {
    337             handler.removeMessages(msg);
    338         } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
    339             return;
    340         }
    341 
    342         handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
    343     }
    344 
    345     private class MediaEventHandler extends Handler {
    346         MediaEventHandler(Looper looper) {
    347             super(looper);
    348         }
    349 
    350         @Override
    351         public void handleMessage(Message msg) {
    352             switch(msg.what) {
    353                 case MSG_RCDISPLAY_CLEAR:
    354                     onRcDisplayClear();
    355                     break;
    356 
    357                 case MSG_RCDISPLAY_UPDATE:
    358                     // msg.obj is guaranteed to be non null
    359                     onRcDisplayUpdate( (PlayerRecord) msg.obj, msg.arg1);
    360                     break;
    361 
    362                 case MSG_REEVALUATE_REMOTE:
    363                     onReevaluateRemote();
    364                     break;
    365 
    366                 case MSG_RCC_NEW_VOLUME_OBS:
    367                     onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
    368                             (IRemoteVolumeObserver)msg.obj /* rvo */);
    369                     break;
    370 
    371                 case MSG_RCDISPLAY_INIT_INFO:
    372                     // msg.obj is guaranteed to be non null
    373                     onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/,
    374                             msg.arg1/*w*/, msg.arg2/*h*/);
    375                     break;
    376 
    377                 case MSG_REEVALUATE_RCD:
    378                     onReevaluateRemoteControlDisplays();
    379                     break;
    380 
    381                 case MSG_UNREGISTER_MEDIABUTTONINTENT:
    382                     unregisterMediaButtonIntent( (PendingIntent) msg.obj );
    383                     break;
    384             }
    385         }
    386     }
    387 
    388 
    389     //==========================================================================================
    390     // AudioFocus
    391     //==========================================================================================
    392 
    393     /* constant to identify focus stack entry that is used to hold the focus while the phone
    394      * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
    395      * entering and exiting calls.
    396      */
    397     protected final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
    398 
    399     private final static Object mAudioFocusLock = new Object();
    400 
    401     private final static Object mRingingLock = new Object();
    402 
    403     private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
    404         @Override
    405         public void onCallStateChanged(int state, String incomingNumber) {
    406             if (state == TelephonyManager.CALL_STATE_RINGING) {
    407                 //Log.v(TAG, " CALL_STATE_RINGING");
    408                 synchronized(mRingingLock) {
    409                     mIsRinging = true;
    410                 }
    411             } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
    412                     || (state == TelephonyManager.CALL_STATE_IDLE)) {
    413                 synchronized(mRingingLock) {
    414                     mIsRinging = false;
    415                 }
    416             }
    417         }
    418     };
    419 
    420     /**
    421      * Discard the current audio focus owner.
    422      * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
    423      * focus), remove it from the stack, and clear the remote control display.
    424      */
    425     protected void discardAudioFocusOwner() {
    426         synchronized(mAudioFocusLock) {
    427             if (!mFocusStack.empty()) {
    428                 // notify the current focus owner it lost focus after removing it from stack
    429                 final FocusRequester exFocusOwner = mFocusStack.pop();
    430                 exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS);
    431                 exFocusOwner.release();
    432             }
    433         }
    434     }
    435 
    436     private void notifyTopOfAudioFocusStack() {
    437         // notify the top of the stack it gained focus
    438         if (!mFocusStack.empty()) {
    439             if (canReassignAudioFocus()) {
    440                 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
    441             }
    442         }
    443     }
    444 
    445     /**
    446      * Focus is requested, propagate the associated loss throughout the stack.
    447      * @param focusGain the new focus gain that will later be added at the top of the stack
    448      */
    449     private void propagateFocusLossFromGain_syncAf(int focusGain) {
    450         // going through the audio focus stack to signal new focus, traversing order doesn't
    451         // matter as all entries respond to the same external focus gain
    452         Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
    453         while(stackIterator.hasNext()) {
    454             stackIterator.next().handleExternalFocusGain(focusGain);
    455         }
    456     }
    457 
    458     private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
    459 
    460     /**
    461      * Helper function:
    462      * Display in the log the current entries in the audio focus stack
    463      */
    464     private void dumpFocusStack(PrintWriter pw) {
    465         pw.println("\nAudio Focus stack entries (last is top of stack):");
    466         synchronized(mAudioFocusLock) {
    467             Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
    468             while(stackIterator.hasNext()) {
    469                 stackIterator.next().dump(pw);
    470             }
    471         }
    472     }
    473 
    474     /**
    475      * Helper function:
    476      * Called synchronized on mAudioFocusLock
    477      * Remove a focus listener from the focus stack.
    478      * @param clientToRemove the focus listener
    479      * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
    480      *   focus, notify the next item in the stack it gained focus.
    481      */
    482     private void removeFocusStackEntry(String clientToRemove, boolean signal) {
    483         // is the current top of the focus stack abandoning focus? (because of request, not death)
    484         if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove))
    485         {
    486             //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
    487             FocusRequester fr = mFocusStack.pop();
    488             fr.release();
    489             if (signal) {
    490                 // notify the new top of the stack it gained focus
    491                 notifyTopOfAudioFocusStack();
    492             }
    493         } else {
    494             // focus is abandoned by a client that's not at the top of the stack,
    495             // no need to update focus.
    496             // (using an iterator on the stack so we can safely remove an entry after having
    497             //  evaluated it, traversal order doesn't matter here)
    498             Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
    499             while(stackIterator.hasNext()) {
    500                 FocusRequester fr = stackIterator.next();
    501                 if(fr.hasSameClient(clientToRemove)) {
    502                     Log.i(TAG, "AudioFocus  removeFocusStackEntry(): removing entry for "
    503                             + clientToRemove);
    504                     stackIterator.remove();
    505                     fr.release();
    506                 }
    507             }
    508         }
    509     }
    510 
    511     /**
    512      * Helper function:
    513      * Called synchronized on mAudioFocusLock
    514      * Remove focus listeners from the focus stack for a particular client when it has died.
    515      */
    516     private void removeFocusStackEntryForClient(IBinder cb) {
    517         // is the owner of the audio focus part of the client to remove?
    518         boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
    519                 mFocusStack.peek().hasSameBinder(cb);
    520         // (using an iterator on the stack so we can safely remove an entry after having
    521         //  evaluated it, traversal order doesn't matter here)
    522         Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
    523         while(stackIterator.hasNext()) {
    524             FocusRequester fr = stackIterator.next();
    525             if(fr.hasSameBinder(cb)) {
    526                 Log.i(TAG, "AudioFocus  removeFocusStackEntry(): removing entry for " + cb);
    527                 stackIterator.remove();
    528                 // the client just died, no need to unlink to its death
    529             }
    530         }
    531         if (isTopOfStackForClientToRemove) {
    532             // we removed an entry at the top of the stack:
    533             //  notify the new top of the stack it gained focus.
    534             notifyTopOfAudioFocusStack();
    535         }
    536     }
    537 
    538     /**
    539      * Helper function:
    540      * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
    541      */
    542     private boolean canReassignAudioFocus() {
    543         // focus requests are rejected during a phone call or when the phone is ringing
    544         // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
    545         if (!mFocusStack.isEmpty() && mFocusStack.peek().hasSameClient(IN_VOICE_COMM_FOCUS_ID)) {
    546             return false;
    547         }
    548         return true;
    549     }
    550 
    551     /**
    552      * Inner class to monitor audio focus client deaths, and remove them from the audio focus
    553      * stack if necessary.
    554      */
    555     protected class AudioFocusDeathHandler implements IBinder.DeathRecipient {
    556         private IBinder mCb; // To be notified of client's death
    557 
    558         AudioFocusDeathHandler(IBinder cb) {
    559             mCb = cb;
    560         }
    561 
    562         public void binderDied() {
    563             synchronized(mAudioFocusLock) {
    564                 Log.w(TAG, "  AudioFocus   audio focus client died");
    565                 removeFocusStackEntryForClient(mCb);
    566             }
    567         }
    568 
    569         public IBinder getBinder() {
    570             return mCb;
    571         }
    572     }
    573 
    574     protected int getCurrentAudioFocus() {
    575         synchronized(mAudioFocusLock) {
    576             if (mFocusStack.empty()) {
    577                 return AudioManager.AUDIOFOCUS_NONE;
    578             } else {
    579                 return mFocusStack.peek().getGainRequest();
    580             }
    581         }
    582     }
    583 
    584     /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int)  */
    585     protected int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
    586             IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
    587         Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
    588         // we need a valid binder callback for clients
    589         if (!cb.pingBinder()) {
    590             Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
    591             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    592         }
    593 
    594         if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
    595                 callingPackageName) != AppOpsManager.MODE_ALLOWED) {
    596             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    597         }
    598 
    599         synchronized(mAudioFocusLock) {
    600             if (!canReassignAudioFocus()) {
    601                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    602             }
    603 
    604             // handle the potential premature death of the new holder of the focus
    605             // (premature death == death before abandoning focus)
    606             // Register for client death notification
    607             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
    608             try {
    609                 cb.linkToDeath(afdh, 0);
    610             } catch (RemoteException e) {
    611                 // client has already died!
    612                 Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
    613                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    614             }
    615 
    616             if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
    617                 // if focus is already owned by this client and the reason for acquiring the focus
    618                 // hasn't changed, don't do anything
    619                 if (mFocusStack.peek().getGainRequest() == focusChangeHint) {
    620                     // unlink death handler so it can be gc'ed.
    621                     // linkToDeath() creates a JNI global reference preventing collection.
    622                     cb.unlinkToDeath(afdh, 0);
    623                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    624                 }
    625                 // the reason for the audio focus request has changed: remove the current top of
    626                 // stack and respond as if we had a new focus owner
    627                 FocusRequester fr = mFocusStack.pop();
    628                 fr.release();
    629             }
    630 
    631             // focus requester might already be somewhere below in the stack, remove it
    632             removeFocusStackEntry(clientId, false /* signal */);
    633 
    634             // propagate the focus change through the stack
    635             if (!mFocusStack.empty()) {
    636                 propagateFocusLossFromGain_syncAf(focusChangeHint);
    637             }
    638 
    639             // push focus requester at the top of the audio focus stack
    640             mFocusStack.push(new FocusRequester(mainStreamType, focusChangeHint, fd, cb,
    641                     clientId, afdh, callingPackageName, Binder.getCallingUid()));
    642 
    643         }//synchronized(mAudioFocusLock)
    644 
    645         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    646     }
    647 
    648     /** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener)  */
    649     protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
    650         Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
    651         try {
    652             // this will take care of notifying the new focus owner if needed
    653             synchronized(mAudioFocusLock) {
    654                 removeFocusStackEntry(clientId, true /*signal*/);
    655             }
    656         } catch (java.util.ConcurrentModificationException cme) {
    657             // Catching this exception here is temporary. It is here just to prevent
    658             // a crash seen when the "Silent" notification is played. This is believed to be fixed
    659             // but this try catch block is left just to be safe.
    660             Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
    661             cme.printStackTrace();
    662         }
    663 
    664         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    665     }
    666 
    667 
    668     protected void unregisterAudioFocusClient(String clientId) {
    669         synchronized(mAudioFocusLock) {
    670             removeFocusStackEntry(clientId, false);
    671         }
    672     }
    673 
    674 
    675     //==========================================================================================
    676     // RemoteControl
    677     //==========================================================================================
    678     /**
    679      * No-op if the key code for keyEvent is not a valid media key
    680      * (see {@link #isValidMediaKeyEvent(KeyEvent)})
    681      * @param keyEvent the key event to send
    682      */
    683     protected void dispatchMediaKeyEvent(KeyEvent keyEvent) {
    684         filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
    685     }
    686 
    687     /**
    688      * No-op if the key code for keyEvent is not a valid media key
    689      * (see {@link #isValidMediaKeyEvent(KeyEvent)})
    690      * @param keyEvent the key event to send
    691      */
    692     protected void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
    693         filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
    694     }
    695 
    696     private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
    697         // sanity check on the incoming key event
    698         if (!isValidMediaKeyEvent(keyEvent)) {
    699             Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
    700             return;
    701         }
    702         // event filtering for telephony
    703         synchronized(mRingingLock) {
    704             synchronized(mPRStack) {
    705                 if ((mMediaReceiverForCalls != null) &&
    706                         (mIsRinging || (mAudioService.getMode() == AudioSystem.MODE_IN_CALL))) {
    707                     dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
    708                     return;
    709                 }
    710             }
    711         }
    712         // event filtering based on voice-based interactions
    713         if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
    714             filterVoiceInputKeyEvent(keyEvent, needWakeLock);
    715         } else {
    716             dispatchMediaKeyEvent(keyEvent, needWakeLock);
    717         }
    718     }
    719 
    720     /**
    721      * Handles the dispatching of the media button events to the telephony package.
    722      * Precondition: mMediaReceiverForCalls != null
    723      * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
    724      * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
    725      *     is dispatched.
    726      */
    727     private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
    728         Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
    729         keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
    730         keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
    731         if (needWakeLock) {
    732             mMediaEventWakeLock.acquire();
    733             keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
    734         }
    735         final long ident = Binder.clearCallingIdentity();
    736         try {
    737             mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
    738                     null, mKeyEventDone, mEventHandler, Activity.RESULT_OK, null, null);
    739         } finally {
    740             Binder.restoreCallingIdentity(ident);
    741         }
    742     }
    743 
    744     /**
    745      * Handles the dispatching of the media button events to one of the registered listeners,
    746      * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
    747      * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
    748      * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
    749      *     is dispatched.
    750      */
    751     private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
    752         if (needWakeLock) {
    753             mMediaEventWakeLock.acquire();
    754         }
    755         Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
    756         keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
    757         synchronized(mPRStack) {
    758             if (!mPRStack.empty()) {
    759                 // send the intent that was registered by the client
    760                 try {
    761                     mPRStack.peek().getMediaButtonIntent().send(mContext,
    762                             needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
    763                             keyIntent, this, mEventHandler);
    764                 } catch (CanceledException e) {
    765                     Log.e(TAG, "Error sending pending intent " + mPRStack.peek());
    766                     e.printStackTrace();
    767                 }
    768             } else {
    769                 // legacy behavior when nobody registered their media button event receiver
    770                 //    through AudioManager
    771                 if (needWakeLock) {
    772                     keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
    773                 }
    774                 final long ident = Binder.clearCallingIdentity();
    775                 try {
    776                     mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
    777                             null, mKeyEventDone,
    778                             mEventHandler, Activity.RESULT_OK, null, null);
    779                 } finally {
    780                     Binder.restoreCallingIdentity(ident);
    781                 }
    782             }
    783         }
    784     }
    785 
    786     /**
    787      * The different actions performed in response to a voice button key event.
    788      */
    789     private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
    790     private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
    791     private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
    792 
    793     private final Object mVoiceEventLock = new Object();
    794     private boolean mVoiceButtonDown;
    795     private boolean mVoiceButtonHandled;
    796 
    797     /**
    798      * Filter key events that may be used for voice-based interactions
    799      * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
    800      *    media buttons that can be used to trigger voice-based interactions.
    801      * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
    802      *     is dispatched.
    803      */
    804     private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
    805         if (DEBUG_RC) {
    806             Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
    807         }
    808 
    809         int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
    810         int keyAction = keyEvent.getAction();
    811         synchronized (mVoiceEventLock) {
    812             if (keyAction == KeyEvent.ACTION_DOWN) {
    813                 if (keyEvent.getRepeatCount() == 0) {
    814                     // initial down
    815                     mVoiceButtonDown = true;
    816                     mVoiceButtonHandled = false;
    817                 } else if (mVoiceButtonDown && !mVoiceButtonHandled
    818                         && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
    819                     // long-press, start voice-based interactions
    820                     mVoiceButtonHandled = true;
    821                     voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
    822                 }
    823             } else if (keyAction == KeyEvent.ACTION_UP) {
    824                 if (mVoiceButtonDown) {
    825                     // voice button up
    826                     mVoiceButtonDown = false;
    827                     if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
    828                         voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
    829                     }
    830                 }
    831             }
    832         }//synchronized (mVoiceEventLock)
    833 
    834         // take action after media button event filtering for voice-based interactions
    835         switch (voiceButtonAction) {
    836             case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
    837                 if (DEBUG_RC) Log.v(TAG, "   ignore key event");
    838                 break;
    839             case VOICEBUTTON_ACTION_START_VOICE_INPUT:
    840                 if (DEBUG_RC) Log.v(TAG, "   start voice-based interactions");
    841                 // then start the voice-based interactions
    842                 startVoiceBasedInteractions(needWakeLock);
    843                 break;
    844             case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
    845                 if (DEBUG_RC) Log.v(TAG, "   send simulated key event, wakelock=" + needWakeLock);
    846                 sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
    847                 break;
    848         }
    849     }
    850 
    851     private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
    852         // send DOWN event
    853         KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
    854         dispatchMediaKeyEvent(keyEvent, needWakeLock);
    855         // send UP event
    856         keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
    857         dispatchMediaKeyEvent(keyEvent, needWakeLock);
    858 
    859     }
    860 
    861     private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
    862         if (keyEvent == null) {
    863             return false;
    864         }
    865         return KeyEvent.isMediaKey(keyEvent.getKeyCode());
    866     }
    867 
    868     /**
    869      * Checks whether the given key code is one that can trigger the launch of voice-based
    870      *   interactions.
    871      * @param keyCode the key code associated with the key event
    872      * @return true if the key is one of the supported voice-based interaction triggers
    873      */
    874     private static boolean isValidVoiceInputKeyCode(int keyCode) {
    875         if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
    876             return true;
    877         } else {
    878             return false;
    879         }
    880     }
    881 
    882     /**
    883      * Tell the system to start voice-based interactions / voice commands
    884      */
    885     private void startVoiceBasedInteractions(boolean needWakeLock) {
    886         Intent voiceIntent = null;
    887         // select which type of search to launch:
    888         // - screen on and device unlocked: action is ACTION_WEB_SEARCH
    889         // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
    890         //    with EXTRA_SECURE set to true if the device is securely locked
    891         PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
    892         boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
    893         if (!isLocked && pm.isScreenOn()) {
    894             voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
    895             Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
    896         } else {
    897             voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
    898             voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
    899                     isLocked && mKeyguardManager.isKeyguardSecure());
    900             Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
    901         }
    902         // start the search activity
    903         if (needWakeLock) {
    904             mMediaEventWakeLock.acquire();
    905         }
    906         final long identity = Binder.clearCallingIdentity();
    907         try {
    908             if (voiceIntent != null) {
    909                 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
    910                         | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
    911                 mContext.startActivityAsUser(voiceIntent, UserHandle.CURRENT);
    912             }
    913         } catch (ActivityNotFoundException e) {
    914             Log.w(TAG, "No activity for search: " + e);
    915         } finally {
    916             Binder.restoreCallingIdentity(identity);
    917             if (needWakeLock) {
    918                 mMediaEventWakeLock.release();
    919             }
    920         }
    921     }
    922 
    923     private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
    924 
    925     // only set when wakelock was acquired, no need to check value when received
    926     private static final String EXTRA_WAKELOCK_ACQUIRED =
    927             "android.media.AudioService.WAKELOCK_ACQUIRED";
    928 
    929     public void onSendFinished(PendingIntent pendingIntent, Intent intent,
    930             int resultCode, String resultData, Bundle resultExtras) {
    931         if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
    932             mMediaEventWakeLock.release();
    933         }
    934     }
    935 
    936     BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
    937         public void onReceive(Context context, Intent intent) {
    938             if (intent == null) {
    939                 return;
    940             }
    941             Bundle extras = intent.getExtras();
    942             if (extras == null) {
    943                 return;
    944             }
    945             if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
    946                 mMediaEventWakeLock.release();
    947             }
    948         }
    949     };
    950 
    951     /**
    952      * Synchronization on mCurrentRcLock always inside a block synchronized on mPRStack
    953      */
    954     private final Object mCurrentRcLock = new Object();
    955     /**
    956      * The one remote control client which will receive a request for display information.
    957      * This object may be null.
    958      * Access protected by mCurrentRcLock.
    959      */
    960     private IRemoteControlClient mCurrentRcClient = null;
    961     /**
    962      * The PendingIntent associated with mCurrentRcClient. Its value is irrelevant
    963      * if mCurrentRcClient is null
    964      */
    965     private PendingIntent mCurrentRcClientIntent = null;
    966 
    967     private final static int RC_INFO_NONE = 0;
    968     private final static int RC_INFO_ALL =
    969         RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
    970         RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
    971         RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
    972         RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
    973 
    974     /**
    975      * A monotonically increasing generation counter for mCurrentRcClient.
    976      * Only accessed with a lock on mCurrentRcLock.
    977      * No value wrap-around issues as we only act on equal values.
    978      */
    979     private int mCurrentRcClientGen = 0;
    980 
    981 
    982     /**
    983      * Internal cache for the playback information of the RemoteControlClient whose volume gets to
    984      * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
    985      * every time we need this info.
    986      */
    987     private RemotePlaybackState mMainRemote;
    988     /**
    989      * Indicates whether the "main" RemoteControlClient is considered active.
    990      * Use synchronized on mMainRemote.
    991      */
    992     private boolean mMainRemoteIsActive;
    993     /**
    994      * Indicates whether there is remote playback going on. True even if there is no "active"
    995      * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
    996      * handles remote playback.
    997      * Use synchronized on mMainRemote.
    998      */
    999     private boolean mHasRemotePlayback;
   1000 
   1001     /**
   1002      * The stack of remote control event receivers.
   1003      * All read and write operations on mPRStack are synchronized.
   1004      */
   1005     private final Stack<PlayerRecord> mPRStack = new Stack<PlayerRecord>();
   1006 
   1007     /**
   1008      * The component the telephony package can register so telephony calls have priority to
   1009      * handle media button events
   1010      */
   1011     private ComponentName mMediaReceiverForCalls = null;
   1012 
   1013     /**
   1014      * Helper function:
   1015      * Display in the log the current entries in the remote control focus stack
   1016      */
   1017     private void dumpRCStack(PrintWriter pw) {
   1018         pw.println("\nRemote Control stack entries (last is top of stack):");
   1019         synchronized(mPRStack) {
   1020             Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
   1021             while(stackIterator.hasNext()) {
   1022                 stackIterator.next().dump(pw, true);
   1023             }
   1024         }
   1025     }
   1026 
   1027     /**
   1028      * Helper function:
   1029      * Display in the log the current entries in the remote control stack, focusing
   1030      * on RemoteControlClient data
   1031      */
   1032     private void dumpRCCStack(PrintWriter pw) {
   1033         pw.println("\nRemote Control Client stack entries (last is top of stack):");
   1034         synchronized(mPRStack) {
   1035             Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
   1036             while(stackIterator.hasNext()) {
   1037                 stackIterator.next().dump(pw, false);
   1038             }
   1039             synchronized(mCurrentRcLock) {
   1040                 pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen);
   1041             }
   1042         }
   1043         synchronized (mMainRemote) {
   1044             pw.println("\nRemote Volume State:");
   1045             pw.println("  has remote: " + mHasRemotePlayback);
   1046             pw.println("  is remote active: " + mMainRemoteIsActive);
   1047             pw.println("  rccId: " + mMainRemote.mRccId);
   1048             pw.println("  volume handling: "
   1049                     + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
   1050                             "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
   1051             pw.println("  volume: " + mMainRemote.mVolume);
   1052             pw.println("  volume steps: " + mMainRemote.mVolumeMax);
   1053         }
   1054     }
   1055 
   1056     /**
   1057      * Helper function:
   1058      * Display in the log the current entries in the list of remote control displays
   1059      */
   1060     private void dumpRCDList(PrintWriter pw) {
   1061         pw.println("\nRemote Control Display list entries:");
   1062         synchronized(mPRStack) {
   1063             final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   1064             while (displayIterator.hasNext()) {
   1065                 final DisplayInfoForServer di = displayIterator.next();
   1066                 pw.println("  IRCD: " + di.mRcDisplay +
   1067                         "  -- w:" + di.mArtworkExpectedWidth +
   1068                         "  -- h:" + di.mArtworkExpectedHeight +
   1069                         "  -- wantsPosSync:" + di.mWantsPositionSync +
   1070                         "  -- " + (di.mEnabled ? "enabled" : "disabled"));
   1071             }
   1072         }
   1073     }
   1074 
   1075     /**
   1076      * Helper function:
   1077      * Push the new media button receiver "near" the top of the PlayerRecord stack.
   1078      * "Near the top" is defined as:
   1079      *   - at the top if the current PlayerRecord at the top is not playing
   1080      *   - below the entries at the top of the stack that correspond to the playing PlayerRecord
   1081      *     otherwise
   1082      * Called synchronized on mPRStack
   1083      * precondition: mediaIntent != null
   1084      * @return true if the top of mPRStack was changed, false otherwise
   1085      */
   1086     private boolean pushMediaButtonReceiver_syncPrs(PendingIntent mediaIntent,
   1087             ComponentName target, IBinder token) {
   1088         if (mPRStack.empty()) {
   1089             mPRStack.push(new PlayerRecord(mediaIntent, target, token));
   1090             return true;
   1091         } else if (mPRStack.peek().hasMatchingMediaButtonIntent(mediaIntent)) {
   1092             // already at top of stack
   1093             return false;
   1094         }
   1095         if (mAppOps.noteOp(AppOpsManager.OP_TAKE_MEDIA_BUTTONS, Binder.getCallingUid(),
   1096                 mediaIntent.getCreatorPackage()) != AppOpsManager.MODE_ALLOWED) {
   1097             return false;
   1098         }
   1099         PlayerRecord oldTopPrse = mPRStack.lastElement(); // top of the stack before any changes
   1100         boolean topChanged = false;
   1101         PlayerRecord prse = null;
   1102         int lastPlayingIndex = mPRStack.size();
   1103         int inStackIndex = -1;
   1104         try {
   1105             // go through the stack from the top to figure out who's playing, and the position
   1106             // of this media button receiver (note that it may not be in the stack)
   1107             for (int index = mPRStack.size()-1; index >= 0; index--) {
   1108                 prse = mPRStack.elementAt(index);
   1109                 if (prse.isPlaybackActive()) {
   1110                     lastPlayingIndex = index;
   1111                 }
   1112                 if (prse.hasMatchingMediaButtonIntent(mediaIntent)) {
   1113                     inStackIndex = index;
   1114                 }
   1115             }
   1116 
   1117             if (inStackIndex == -1) {
   1118                 // is not in stack
   1119                 prse = new PlayerRecord(mediaIntent, target, token);
   1120                 // it's new so it's not playing (no RemoteControlClient to give a playstate),
   1121                 // therefore it goes after the ones with active playback
   1122                 mPRStack.add(lastPlayingIndex, prse);
   1123             } else {
   1124                 // is in the stack
   1125                 if (mPRStack.size() > 1) { // no need to remove and add if stack contains only 1
   1126                     prse = mPRStack.elementAt(inStackIndex);
   1127                     // remove it from its old location in the stack
   1128                     mPRStack.removeElementAt(inStackIndex);
   1129                     if (prse.isPlaybackActive()) {
   1130                         // and put it at the top
   1131                         mPRStack.push(prse);
   1132                     } else {
   1133                         // and put it after the ones with active playback
   1134                         if (inStackIndex > lastPlayingIndex) {
   1135                             mPRStack.add(lastPlayingIndex, prse);
   1136                         } else {
   1137                             mPRStack.add(lastPlayingIndex - 1, prse);
   1138                         }
   1139                     }
   1140                 }
   1141             }
   1142 
   1143         } catch (ArrayIndexOutOfBoundsException e) {
   1144             // not expected to happen, indicates improper concurrent modification or bad index
   1145             Log.e(TAG, "Wrong index (inStack=" + inStackIndex + " lastPlaying=" + lastPlayingIndex
   1146                     + " size=" + mPRStack.size()
   1147                     + " accessing media button stack", e);
   1148         }
   1149 
   1150         return (topChanged);
   1151     }
   1152 
   1153     /**
   1154      * Helper function:
   1155      * Remove the remote control receiver from the RC focus stack.
   1156      * Called synchronized on mPRStack
   1157      * precondition: pi != null
   1158      */
   1159     private void removeMediaButtonReceiver_syncPrs(PendingIntent pi) {
   1160         try {
   1161             for (int index = mPRStack.size()-1; index >= 0; index--) {
   1162                 final PlayerRecord prse = mPRStack.elementAt(index);
   1163                 if (prse.hasMatchingMediaButtonIntent(pi)) {
   1164                     prse.destroy();
   1165                     // ok to remove element while traversing the stack since we're leaving the loop
   1166                     mPRStack.removeElementAt(index);
   1167                     break;
   1168                 }
   1169             }
   1170         } catch (ArrayIndexOutOfBoundsException e) {
   1171             // not expected to happen, indicates improper concurrent modification
   1172             Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
   1173         }
   1174     }
   1175 
   1176     /**
   1177      * Helper function:
   1178      * Called synchronized on mPRStack
   1179      */
   1180     private boolean isCurrentRcController(PendingIntent pi) {
   1181         if (!mPRStack.empty() && mPRStack.peek().hasMatchingMediaButtonIntent(pi)) {
   1182             return true;
   1183         }
   1184         return false;
   1185     }
   1186 
   1187     //==========================================================================================
   1188     // Remote control display / client
   1189     //==========================================================================================
   1190     /**
   1191      * Update the remote control displays with the new "focused" client generation
   1192      */
   1193     private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
   1194             PendingIntent newMediaIntent, boolean clearing) {
   1195         synchronized(mPRStack) {
   1196             if (mRcDisplays.size() > 0) {
   1197                 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   1198                 while (displayIterator.hasNext()) {
   1199                     final DisplayInfoForServer di = displayIterator.next();
   1200                     try {
   1201                         di.mRcDisplay.setCurrentClientId(
   1202                                 newClientGeneration, newMediaIntent, clearing);
   1203                     } catch (RemoteException e) {
   1204                         Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc()",e);
   1205                         di.release();
   1206                         displayIterator.remove();
   1207                     }
   1208                 }
   1209             }
   1210         }
   1211     }
   1212 
   1213     /**
   1214      * Update the remote control clients with the new "focused" client generation
   1215      */
   1216     private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
   1217         // (using an iterator on the stack so we can safely remove an entry if needed,
   1218         //  traversal order doesn't matter here as we update all entries)
   1219         Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
   1220         while(stackIterator.hasNext()) {
   1221             PlayerRecord se = stackIterator.next();
   1222             if ((se != null) && (se.getRcc() != null)) {
   1223                 try {
   1224                     se.getRcc().setCurrentClientGenerationId(newClientGeneration);
   1225                 } catch (RemoteException e) {
   1226                     Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()",e);
   1227                     stackIterator.remove();
   1228                     se.unlinkToRcClientDeath();
   1229                 }
   1230             }
   1231         }
   1232     }
   1233 
   1234     /**
   1235      * Update the displays and clients with the new "focused" client generation and name
   1236      * @param newClientGeneration the new generation value matching a client update
   1237      * @param newMediaIntent the media button event receiver associated with the client.
   1238      *    May be null, which implies there is no registered media button event receiver.
   1239      * @param clearing true if the new client generation value maps to a remote control update
   1240      *    where the display should be cleared.
   1241      */
   1242     private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
   1243             PendingIntent newMediaIntent, boolean clearing) {
   1244         // send the new valid client generation ID to all displays
   1245         setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
   1246         // send the new valid client generation ID to all clients
   1247         setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
   1248     }
   1249 
   1250     /**
   1251      * Called when processing MSG_RCDISPLAY_CLEAR event
   1252      */
   1253     private void onRcDisplayClear() {
   1254         if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
   1255 
   1256         synchronized(mPRStack) {
   1257             synchronized(mCurrentRcLock) {
   1258                 mCurrentRcClientGen++;
   1259                 // synchronously update the displays and clients with the new client generation
   1260                 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
   1261                         null /*newMediaIntent*/, true /*clearing*/);
   1262             }
   1263         }
   1264     }
   1265 
   1266     /**
   1267      * Called when processing MSG_RCDISPLAY_UPDATE event
   1268      */
   1269     private void onRcDisplayUpdate(PlayerRecord prse, int flags /* USED ?*/) {
   1270         synchronized(mPRStack) {
   1271             synchronized(mCurrentRcLock) {
   1272                 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(prse.getRcc()))) {
   1273                     if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
   1274 
   1275                     mCurrentRcClientGen++;
   1276                     // synchronously update the displays and clients with
   1277                     //      the new client generation
   1278                     setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
   1279                             prse.getMediaButtonIntent() /*newMediaIntent*/,
   1280                             false /*clearing*/);
   1281 
   1282                     // tell the current client that it needs to send info
   1283                     try {
   1284                         //TODO change name to informationRequestForAllDisplays()
   1285                         mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags);
   1286                     } catch (RemoteException e) {
   1287                         Log.e(TAG, "Current valid remote client is dead: "+e);
   1288                         mCurrentRcClient = null;
   1289                     }
   1290                 } else {
   1291                     // the remote control display owner has changed between the
   1292                     // the message to update the display was sent, and the time it
   1293                     // gets to be processed (now)
   1294                 }
   1295             }
   1296         }
   1297     }
   1298 
   1299     /**
   1300      * Called when processing MSG_RCDISPLAY_INIT_INFO event
   1301      * Causes the current RemoteControlClient to send its info (metadata, playstate...) to
   1302      *   a single RemoteControlDisplay, NOT all of them, as with MSG_RCDISPLAY_UPDATE.
   1303      */
   1304     private void onRcDisplayInitInfo(IRemoteControlDisplay newRcd, int w, int h) {
   1305         synchronized(mPRStack) {
   1306             synchronized(mCurrentRcLock) {
   1307                 if (mCurrentRcClient != null) {
   1308                     if (DEBUG_RC) { Log.i(TAG, "Init RCD with current info"); }
   1309                     try {
   1310                         // synchronously update the new RCD with the current client generation
   1311                         // and matching PendingIntent
   1312                         newRcd.setCurrentClientId(mCurrentRcClientGen, mCurrentRcClientIntent,
   1313                                 false);
   1314 
   1315                         // tell the current RCC that it needs to send info, but only to the new RCD
   1316                         try {
   1317                             mCurrentRcClient.informationRequestForDisplay(newRcd, w, h);
   1318                         } catch (RemoteException e) {
   1319                             Log.e(TAG, "Current valid remote client is dead: ", e);
   1320                             mCurrentRcClient = null;
   1321                         }
   1322                     } catch (RemoteException e) {
   1323                         Log.e(TAG, "Dead display in onRcDisplayInitInfo()", e);
   1324                     }
   1325                 }
   1326             }
   1327         }
   1328     }
   1329 
   1330     /**
   1331      * Helper function:
   1332      * Called synchronized on mPRStack
   1333      */
   1334     private void clearRemoteControlDisplay_syncPrs() {
   1335         synchronized(mCurrentRcLock) {
   1336             mCurrentRcClient = null;
   1337         }
   1338         // will cause onRcDisplayClear() to be called in AudioService's handler thread
   1339         mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
   1340     }
   1341 
   1342     /**
   1343      * Helper function for code readability: only to be called from
   1344      *    checkUpdateRemoteControlDisplay_syncPrs() which checks the preconditions for
   1345      *    this method.
   1346      * Preconditions:
   1347      *    - called synchronized on mPRStack
   1348      *    - mPRStack.isEmpty() is false
   1349      */
   1350     private void updateRemoteControlDisplay_syncPrs(int infoChangedFlags) {
   1351         PlayerRecord prse = mPRStack.peek();
   1352         int infoFlagsAboutToBeUsed = infoChangedFlags;
   1353         // this is where we enforce opt-in for information display on the remote controls
   1354         //   with the new AudioManager.registerRemoteControlClient() API
   1355         if (prse.getRcc() == null) {
   1356             //Log.w(TAG, "Can't update remote control display with null remote control client");
   1357             clearRemoteControlDisplay_syncPrs();
   1358             return;
   1359         }
   1360         synchronized(mCurrentRcLock) {
   1361             if (!prse.getRcc().equals(mCurrentRcClient)) {
   1362                 // new RC client, assume every type of information shall be queried
   1363                 infoFlagsAboutToBeUsed = RC_INFO_ALL;
   1364             }
   1365             mCurrentRcClient = prse.getRcc();
   1366             mCurrentRcClientIntent = prse.getMediaButtonIntent();
   1367         }
   1368         // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
   1369         mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
   1370                 infoFlagsAboutToBeUsed /* arg1 */, 0, prse /* obj, != null */) );
   1371     }
   1372 
   1373     /**
   1374      * Helper function:
   1375      * Called synchronized on mPRStack
   1376      * Check whether the remote control display should be updated, triggers the update if required
   1377      * @param infoChangedFlags the flags corresponding to the remote control client information
   1378      *     that has changed, if applicable (checking for the update conditions might trigger a
   1379      *     clear, rather than an update event).
   1380      */
   1381     private void checkUpdateRemoteControlDisplay_syncPrs(int infoChangedFlags) {
   1382         // determine whether the remote control display should be refreshed
   1383         // if the player record stack is empty, there is nothing to display, so clear the RC display
   1384         if (mPRStack.isEmpty()) {
   1385             clearRemoteControlDisplay_syncPrs();
   1386             return;
   1387         }
   1388 
   1389         // this is where more rules for refresh go
   1390 
   1391         // refresh conditions were verified: update the remote controls
   1392         // ok to call: synchronized on mPRStack, mPRStack is not empty
   1393         updateRemoteControlDisplay_syncPrs(infoChangedFlags);
   1394     }
   1395 
   1396     /**
   1397      * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
   1398      * precondition: mediaIntent != null
   1399      */
   1400     protected void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
   1401             IBinder token) {
   1402         Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
   1403 
   1404         synchronized(mPRStack) {
   1405             if (pushMediaButtonReceiver_syncPrs(mediaIntent, eventReceiver, token)) {
   1406                 // new RC client, assume every type of information shall be queried
   1407                 checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
   1408             }
   1409         }
   1410     }
   1411 
   1412     /**
   1413      * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
   1414      * precondition: mediaIntent != null, eventReceiver != null
   1415      */
   1416     protected void unregisterMediaButtonIntent(PendingIntent mediaIntent)
   1417     {
   1418         Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
   1419 
   1420         synchronized(mPRStack) {
   1421             boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
   1422             removeMediaButtonReceiver_syncPrs(mediaIntent);
   1423             if (topOfStackWillChange) {
   1424                 // current RC client will change, assume every type of info needs to be queried
   1425                 checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
   1426             }
   1427         }
   1428     }
   1429 
   1430     protected void unregisterMediaButtonIntentAsync(final PendingIntent mediaIntent) {
   1431         mEventHandler.sendMessage(
   1432                 mEventHandler.obtainMessage(MSG_UNREGISTER_MEDIABUTTONINTENT, 0, 0,
   1433                         mediaIntent));
   1434     }
   1435 
   1436     /**
   1437      * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
   1438      * precondition: c != null
   1439      */
   1440     protected void registerMediaButtonEventReceiverForCalls(ComponentName c) {
   1441         if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
   1442                 != PackageManager.PERMISSION_GRANTED) {
   1443             Log.e(TAG, "Invalid permissions to register media button receiver for calls");
   1444             return;
   1445         }
   1446         synchronized(mPRStack) {
   1447             mMediaReceiverForCalls = c;
   1448         }
   1449     }
   1450 
   1451     /**
   1452      * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
   1453      */
   1454     protected void unregisterMediaButtonEventReceiverForCalls() {
   1455         if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
   1456                 != PackageManager.PERMISSION_GRANTED) {
   1457             Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
   1458             return;
   1459         }
   1460         synchronized(mPRStack) {
   1461             mMediaReceiverForCalls = null;
   1462         }
   1463     }
   1464 
   1465     /**
   1466      * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
   1467      * @return the unique ID of the PlayerRecord associated with the RemoteControlClient
   1468      * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
   1469      *     without modifying the RC stack, but while still causing the display to refresh (will
   1470      *     become blank as a result of this)
   1471      */
   1472     protected int registerRemoteControlClient(PendingIntent mediaIntent,
   1473             IRemoteControlClient rcClient, String callingPackageName) {
   1474         if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
   1475         int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
   1476         synchronized(mPRStack) {
   1477             // store the new display information
   1478             try {
   1479                 for (int index = mPRStack.size()-1; index >= 0; index--) {
   1480                     final PlayerRecord prse = mPRStack.elementAt(index);
   1481                     if(prse.hasMatchingMediaButtonIntent(mediaIntent)) {
   1482                         prse.resetControllerInfoForRcc(rcClient, callingPackageName,
   1483                                 Binder.getCallingUid());
   1484 
   1485                         if (rcClient == null) {
   1486                             break;
   1487                         }
   1488 
   1489                         rccId = prse.getRccId();
   1490 
   1491                         // there is a new (non-null) client:
   1492                         //     give the new client the displays (if any)
   1493                         if (mRcDisplays.size() > 0) {
   1494                             plugRemoteControlDisplaysIntoClient_syncPrs(prse.getRcc());
   1495                         }
   1496                         break;
   1497                     }
   1498                 }//for
   1499             } catch (ArrayIndexOutOfBoundsException e) {
   1500                 // not expected to happen, indicates improper concurrent modification
   1501                 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
   1502             }
   1503 
   1504             // if the eventReceiver is at the top of the stack
   1505             // then check for potential refresh of the remote controls
   1506             if (isCurrentRcController(mediaIntent)) {
   1507                 checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
   1508             }
   1509         }//synchronized(mPRStack)
   1510         return rccId;
   1511     }
   1512 
   1513     /**
   1514      * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
   1515      * rcClient is guaranteed non-null
   1516      */
   1517     protected void unregisterRemoteControlClient(PendingIntent mediaIntent,
   1518             IRemoteControlClient rcClient) {
   1519         if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient);
   1520         synchronized(mPRStack) {
   1521             boolean topRccChange = false;
   1522             try {
   1523                 for (int index = mPRStack.size()-1; index >= 0; index--) {
   1524                     final PlayerRecord prse = mPRStack.elementAt(index);
   1525                     if ((prse.hasMatchingMediaButtonIntent(mediaIntent))
   1526                             && rcClient.equals(prse.getRcc())) {
   1527                         // we found the IRemoteControlClient to unregister
   1528                         prse.resetControllerInfoForNoRcc();
   1529                         topRccChange = (index == mPRStack.size()-1);
   1530                         // there can only be one matching RCC in the RC stack, we're done
   1531                         break;
   1532                     }
   1533                 }
   1534             } catch (ArrayIndexOutOfBoundsException e) {
   1535                 // not expected to happen, indicates improper concurrent modification
   1536                 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
   1537             }
   1538             if (topRccChange) {
   1539                 // no more RCC for the RCD, check for potential refresh of the remote controls
   1540                 checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
   1541             }
   1542         }
   1543     }
   1544 
   1545 
   1546     /**
   1547      * A class to encapsulate all the information about a remote control display.
   1548      * After instanciation, init() must always be called before the object is added in the list
   1549      * of displays.
   1550      * Before being removed from the list of displays, release() must always be called (otherwise
   1551      * it will leak death handlers).
   1552      */
   1553     private class DisplayInfoForServer implements IBinder.DeathRecipient {
   1554         /** may never be null */
   1555         private final IRemoteControlDisplay mRcDisplay;
   1556         private final IBinder mRcDisplayBinder;
   1557         private int mArtworkExpectedWidth = -1;
   1558         private int mArtworkExpectedHeight = -1;
   1559         private boolean mWantsPositionSync = false;
   1560         private ComponentName mClientNotifListComp;
   1561         private boolean mEnabled = true;
   1562 
   1563         public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {
   1564             if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h);
   1565             mRcDisplay = rcd;
   1566             mRcDisplayBinder = rcd.asBinder();
   1567             mArtworkExpectedWidth = w;
   1568             mArtworkExpectedHeight = h;
   1569         }
   1570 
   1571         public boolean init() {
   1572             try {
   1573                 mRcDisplayBinder.linkToDeath(this, 0);
   1574             } catch (RemoteException e) {
   1575                 // remote control display is DOA, disqualify it
   1576                 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + mRcDisplayBinder);
   1577                 return false;
   1578             }
   1579             return true;
   1580         }
   1581 
   1582         public void release() {
   1583             try {
   1584                 mRcDisplayBinder.unlinkToDeath(this, 0);
   1585             } catch (java.util.NoSuchElementException e) {
   1586                 // not much we can do here, the display should have been unregistered anyway
   1587                 Log.e(TAG, "Error in DisplaInfoForServer.relase()", e);
   1588             }
   1589         }
   1590 
   1591         public void binderDied() {
   1592             synchronized(mPRStack) {
   1593                 Log.w(TAG, "RemoteControl: display " + mRcDisplay + " died");
   1594                 // remove the display from the list
   1595                 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   1596                 while (displayIterator.hasNext()) {
   1597                     final DisplayInfoForServer di = displayIterator.next();
   1598                     if (di.mRcDisplay == mRcDisplay) {
   1599                         if (DEBUG_RC) Log.w(TAG, " RCD removed from list");
   1600                         displayIterator.remove();
   1601                         return;
   1602                     }
   1603                 }
   1604             }
   1605         }
   1606     }
   1607 
   1608     /**
   1609      * The remote control displays.
   1610      * Access synchronized on mPRStack
   1611      */
   1612     private ArrayList<DisplayInfoForServer> mRcDisplays = new ArrayList<DisplayInfoForServer>(1);
   1613 
   1614     /**
   1615      * Plug each registered display into the specified client
   1616      * @param rcc, guaranteed non null
   1617      */
   1618     private void plugRemoteControlDisplaysIntoClient_syncPrs(IRemoteControlClient rcc) {
   1619         final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   1620         while (displayIterator.hasNext()) {
   1621             final DisplayInfoForServer di = displayIterator.next();
   1622             try {
   1623                 rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth,
   1624                         di.mArtworkExpectedHeight);
   1625                 if (di.mWantsPositionSync) {
   1626                     rcc.setWantsSyncForDisplay(di.mRcDisplay, true);
   1627                 }
   1628             } catch (RemoteException e) {
   1629                 Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
   1630             }
   1631         }
   1632     }
   1633 
   1634     private void enableRemoteControlDisplayForClient_syncRcStack(IRemoteControlDisplay rcd,
   1635             boolean enabled) {
   1636         // let all the remote control clients know whether the given display is enabled
   1637         //   (so the remote control stack traversal order doesn't matter).
   1638         final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
   1639         while(stackIterator.hasNext()) {
   1640             PlayerRecord prse = stackIterator.next();
   1641             if(prse.getRcc() != null) {
   1642                 try {
   1643                     prse.getRcc().enableRemoteControlDisplay(rcd, enabled);
   1644                 } catch (RemoteException e) {
   1645                     Log.e(TAG, "Error connecting RCD to client: ", e);
   1646                 }
   1647             }
   1648         }
   1649     }
   1650 
   1651     /**
   1652      * Is the remote control display interface already registered
   1653      * @param rcd
   1654      * @return true if the IRemoteControlDisplay is already in the list of displays
   1655      */
   1656     private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) {
   1657         final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   1658         while (displayIterator.hasNext()) {
   1659             final DisplayInfoForServer di = displayIterator.next();
   1660             if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
   1661                 return true;
   1662             }
   1663         }
   1664         return false;
   1665     }
   1666 
   1667     /**
   1668      * Register an IRemoteControlDisplay.
   1669      * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
   1670      * at the top of the stack to update the new display with its information.
   1671      * @see android.media.IAudioService#registerRemoteControlDisplay(android.media.IRemoteControlDisplay, int, int)
   1672      * @param rcd the IRemoteControlDisplay to register. No effect if null.
   1673      * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
   1674      *   display doesn't need to receive artwork.
   1675      * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
   1676      *   display doesn't need to receive artwork.
   1677      * @param listenerComp the component for the listener interface, may be null if it's not needed
   1678      *   to verify it belongs to one of the enabled notification listeners
   1679      */
   1680     private void registerRemoteControlDisplay_int(IRemoteControlDisplay rcd, int w, int h,
   1681             ComponentName listenerComp) {
   1682         if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
   1683         synchronized(mAudioFocusLock) {
   1684             synchronized(mPRStack) {
   1685                 if ((rcd == null) || rcDisplayIsPluggedIn_syncRcStack(rcd)) {
   1686                     return;
   1687                 }
   1688                 DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h);
   1689                 di.mEnabled = true;
   1690                 di.mClientNotifListComp = listenerComp;
   1691                 if (!di.init()) {
   1692                     if (DEBUG_RC) Log.e(TAG, " error registering RCD");
   1693                     return;
   1694                 }
   1695                 // add RCD to list of displays
   1696                 mRcDisplays.add(di);
   1697 
   1698                 // let all the remote control clients know there is a new display (so the remote
   1699                 //   control stack traversal order doesn't matter).
   1700                 Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
   1701                 while(stackIterator.hasNext()) {
   1702                     PlayerRecord prse = stackIterator.next();
   1703                     if(prse.getRcc() != null) {
   1704                         try {
   1705                             prse.getRcc().plugRemoteControlDisplay(rcd, w, h);
   1706                         } catch (RemoteException e) {
   1707                             Log.e(TAG, "Error connecting RCD to client: ", e);
   1708                         }
   1709                     }
   1710                 }
   1711 
   1712                 // we have a new display, of which all the clients are now aware: have it be
   1713                 // initialized wih the current gen ID and the current client info, do not
   1714                 // reset the information for the other (existing) displays
   1715                 sendMsg(mEventHandler, MSG_RCDISPLAY_INIT_INFO, SENDMSG_QUEUE,
   1716                         w /*arg1*/, h /*arg2*/,
   1717                         rcd /*obj*/, 0/*delay*/);
   1718             }
   1719         }
   1720     }
   1721 
   1722     /**
   1723      * Unregister an IRemoteControlDisplay.
   1724      * No effect if the IRemoteControlDisplay hasn't been successfully registered.
   1725      * @see android.media.IAudioService#unregisterRemoteControlDisplay(android.media.IRemoteControlDisplay)
   1726      * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
   1727      */
   1728     protected void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
   1729         if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
   1730         synchronized(mPRStack) {
   1731             if (rcd == null) {
   1732                 return;
   1733             }
   1734 
   1735             boolean displayWasPluggedIn = false;
   1736             final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   1737             while (displayIterator.hasNext() && !displayWasPluggedIn) {
   1738                 final DisplayInfoForServer di = displayIterator.next();
   1739                 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
   1740                     displayWasPluggedIn = true;
   1741                     di.release();
   1742                     displayIterator.remove();
   1743                 }
   1744             }
   1745 
   1746             if (displayWasPluggedIn) {
   1747                 // disconnect this remote control display from all the clients, so the remote
   1748                 //   control stack traversal order doesn't matter
   1749                 final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
   1750                 while(stackIterator.hasNext()) {
   1751                     final PlayerRecord prse = stackIterator.next();
   1752                     if(prse.getRcc() != null) {
   1753                         try {
   1754                             prse.getRcc().unplugRemoteControlDisplay(rcd);
   1755                         } catch (RemoteException e) {
   1756                             Log.e(TAG, "Error disconnecting remote control display to client: ", e);
   1757                         }
   1758                     }
   1759                 }
   1760             } else {
   1761                 if (DEBUG_RC) Log.w(TAG, "  trying to unregister unregistered RCD");
   1762             }
   1763         }
   1764     }
   1765 
   1766     /**
   1767      * Update the size of the artwork used by an IRemoteControlDisplay.
   1768      * @see android.media.IAudioService#remoteControlDisplayUsesBitmapSize(android.media.IRemoteControlDisplay, int, int)
   1769      * @param rcd the IRemoteControlDisplay with the new artwork size requirement
   1770      * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
   1771      *   display doesn't need to receive artwork.
   1772      * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
   1773      *   display doesn't need to receive artwork.
   1774      */
   1775     protected void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
   1776         synchronized(mPRStack) {
   1777             final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   1778             boolean artworkSizeUpdate = false;
   1779             while (displayIterator.hasNext() && !artworkSizeUpdate) {
   1780                 final DisplayInfoForServer di = displayIterator.next();
   1781                 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
   1782                     if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
   1783                         di.mArtworkExpectedWidth = w;
   1784                         di.mArtworkExpectedHeight = h;
   1785                         artworkSizeUpdate = true;
   1786                     }
   1787                 }
   1788             }
   1789             if (artworkSizeUpdate) {
   1790                 // RCD is currently plugged in and its artwork size has changed, notify all RCCs,
   1791                 // stack traversal order doesn't matter
   1792                 final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
   1793                 while(stackIterator.hasNext()) {
   1794                     final PlayerRecord prse = stackIterator.next();
   1795                     if(prse.getRcc() != null) {
   1796                         try {
   1797                             prse.getRcc().setBitmapSizeForDisplay(rcd, w, h);
   1798                         } catch (RemoteException e) {
   1799                             Log.e(TAG, "Error setting bitmap size for RCD on RCC: ", e);
   1800                         }
   1801                     }
   1802                 }
   1803             }
   1804         }
   1805     }
   1806 
   1807     /**
   1808      * Controls whether a remote control display needs periodic checks of the RemoteControlClient
   1809      * playback position to verify that the estimated position has not drifted from the actual
   1810      * position. By default the check is not performed.
   1811      * The IRemoteControlDisplay must have been previously registered for this to have any effect.
   1812      * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
   1813      *     or disabled. Not null.
   1814      * @param wantsSync if true, RemoteControlClient instances which expose their playback position
   1815      *     to the framework will regularly compare the estimated playback position with the actual
   1816      *     position, and will update the IRemoteControlDisplay implementation whenever a drift is
   1817      *     detected.
   1818      */
   1819     protected void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
   1820             boolean wantsSync) {
   1821         synchronized(mPRStack) {
   1822             boolean rcdRegistered = false;
   1823             // store the information about this display
   1824             // (display stack traversal order doesn't matter).
   1825             final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   1826             while (displayIterator.hasNext()) {
   1827                 final DisplayInfoForServer di = displayIterator.next();
   1828                 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
   1829                     di.mWantsPositionSync = wantsSync;
   1830                     rcdRegistered = true;
   1831                     break;
   1832                 }
   1833             }
   1834             if (!rcdRegistered) {
   1835                 return;
   1836             }
   1837             // notify all current RemoteControlClients
   1838             // (stack traversal order doesn't matter as we notify all RCCs)
   1839             final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
   1840             while (stackIterator.hasNext()) {
   1841                 final PlayerRecord prse = stackIterator.next();
   1842                 if (prse.getRcc() != null) {
   1843                     try {
   1844                         prse.getRcc().setWantsSyncForDisplay(rcd, wantsSync);
   1845                     } catch (RemoteException e) {
   1846                         Log.e(TAG, "Error setting position sync flag for RCD on RCC: ", e);
   1847                     }
   1848                 }
   1849             }
   1850         }
   1851     }
   1852 
   1853     // handler for MSG_RCC_NEW_VOLUME_OBS
   1854     private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
   1855         synchronized(mPRStack) {
   1856             // The stack traversal order doesn't matter because there is only one stack entry
   1857             //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
   1858             //  start iterating from the top.
   1859             try {
   1860                 for (int index = mPRStack.size()-1; index >= 0; index--) {
   1861                     final PlayerRecord prse = mPRStack.elementAt(index);
   1862                     if (prse.getRccId() == rccId) {
   1863                         prse.mRemoteVolumeObs = rvo;
   1864                         break;
   1865                     }
   1866                 }
   1867             } catch (ArrayIndexOutOfBoundsException e) {
   1868                 // not expected to happen, indicates improper concurrent modification
   1869                 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
   1870             }
   1871         }
   1872     }
   1873 
   1874     /**
   1875      * Checks if a remote client is active on the supplied stream type. Update the remote stream
   1876      * volume state if found and playing
   1877      * @param streamType
   1878      * @return false if no remote playing is currently playing
   1879      */
   1880     protected boolean checkUpdateRemoteStateIfActive(int streamType) {
   1881         synchronized(mPRStack) {
   1882             // iterating from top of stack as active playback is more likely on entries at the top
   1883             try {
   1884                 for (int index = mPRStack.size()-1; index >= 0; index--) {
   1885                     final PlayerRecord prse = mPRStack.elementAt(index);
   1886                     if ((prse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
   1887                             && isPlaystateActive(prse.mPlaybackState.mState)
   1888                             && (prse.mPlaybackStream == streamType)) {
   1889                         if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
   1890                                 + ", vol =" + prse.mPlaybackVolume);
   1891                         synchronized (mMainRemote) {
   1892                             mMainRemote.mRccId = prse.getRccId();
   1893                             mMainRemote.mVolume = prse.mPlaybackVolume;
   1894                             mMainRemote.mVolumeMax = prse.mPlaybackVolumeMax;
   1895                             mMainRemote.mVolumeHandling = prse.mPlaybackVolumeHandling;
   1896                             mMainRemoteIsActive = true;
   1897                         }
   1898                         return true;
   1899                     }
   1900                 }
   1901             } catch (ArrayIndexOutOfBoundsException e) {
   1902                 // not expected to happen, indicates improper concurrent modification
   1903                 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
   1904             }
   1905         }
   1906         synchronized (mMainRemote) {
   1907             mMainRemoteIsActive = false;
   1908         }
   1909         return false;
   1910     }
   1911 
   1912     /**
   1913      * Returns true if the given playback state is considered "active", i.e. it describes a state
   1914      * where playback is happening, or about to
   1915      * @param playState the playback state to evaluate
   1916      * @return true if active, false otherwise (inactive or unknown)
   1917      */
   1918     protected static boolean isPlaystateActive(int playState) {
   1919         switch (playState) {
   1920             case RemoteControlClient.PLAYSTATE_PLAYING:
   1921             case RemoteControlClient.PLAYSTATE_BUFFERING:
   1922             case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
   1923             case RemoteControlClient.PLAYSTATE_REWINDING:
   1924             case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
   1925             case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
   1926                 return true;
   1927             default:
   1928                 return false;
   1929         }
   1930     }
   1931 
   1932     private void sendVolumeUpdateToRemote(int rccId, int direction) {
   1933         if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
   1934         if (direction == 0) {
   1935             // only handling discrete events
   1936             return;
   1937         }
   1938         IRemoteVolumeObserver rvo = null;
   1939         synchronized (mPRStack) {
   1940             // The stack traversal order doesn't matter because there is only one stack entry
   1941             //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
   1942             //  start iterating from the top.
   1943             try {
   1944                 for (int index = mPRStack.size()-1; index >= 0; index--) {
   1945                     final PlayerRecord prse = mPRStack.elementAt(index);
   1946                     //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
   1947                     if (prse.getRccId() == rccId) {
   1948                         rvo = prse.mRemoteVolumeObs;
   1949                         break;
   1950                     }
   1951                 }
   1952             } catch (ArrayIndexOutOfBoundsException e) {
   1953                 // not expected to happen, indicates improper concurrent modification
   1954                 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
   1955             }
   1956         }
   1957         if (rvo != null) {
   1958             try {
   1959                 rvo.dispatchRemoteVolumeUpdate(direction, -1);
   1960             } catch (RemoteException e) {
   1961                 Log.e(TAG, "Error dispatching relative volume update", e);
   1962             }
   1963         }
   1964     }
   1965 
   1966     protected int getRemoteStreamMaxVolume() {
   1967         synchronized (mMainRemote) {
   1968             if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
   1969                 return 0;
   1970             }
   1971             return mMainRemote.mVolumeMax;
   1972         }
   1973     }
   1974 
   1975     protected int getRemoteStreamVolume() {
   1976         synchronized (mMainRemote) {
   1977             if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
   1978                 return 0;
   1979             }
   1980             return mMainRemote.mVolume;
   1981         }
   1982     }
   1983 
   1984     protected void setRemoteStreamVolume(int vol) {
   1985         if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
   1986         int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
   1987         synchronized (mMainRemote) {
   1988             if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
   1989                 return;
   1990             }
   1991             rccId = mMainRemote.mRccId;
   1992         }
   1993         IRemoteVolumeObserver rvo = null;
   1994         synchronized (mPRStack) {
   1995             // The stack traversal order doesn't matter because there is only one stack entry
   1996             //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
   1997             //  start iterating from the top.
   1998             try {
   1999                 for (int index = mPRStack.size()-1; index >= 0; index--) {
   2000                     final PlayerRecord prse = mPRStack.elementAt(index);
   2001                     //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
   2002                     if (prse.getRccId() == rccId) {
   2003                         rvo = prse.mRemoteVolumeObs;
   2004                         break;
   2005                     }
   2006                 }
   2007             } catch (ArrayIndexOutOfBoundsException e) {
   2008                 // not expected to happen, indicates improper concurrent modification
   2009                 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
   2010             }
   2011         }
   2012         if (rvo != null) {
   2013             try {
   2014                 rvo.dispatchRemoteVolumeUpdate(0, vol);
   2015             } catch (RemoteException e) {
   2016                 Log.e(TAG, "Error dispatching absolute volume update", e);
   2017             }
   2018         }
   2019     }
   2020 
   2021     /**
   2022      * Call to make AudioService reevaluate whether it's in a mode where remote players should
   2023      * have their volume controlled. In this implementation this is only to reset whether
   2024      * VolumePanel should display remote volumes
   2025      */
   2026     protected void postReevaluateRemote() {
   2027         sendMsg(mEventHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
   2028     }
   2029 
   2030     private void onReevaluateRemote() {
   2031         // TODO This was used to notify VolumePanel if there was remote playback
   2032         // in the stack. This is now in MediaSessionService. More code should be
   2033         // removed.
   2034     }
   2035 
   2036 }
   2037