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