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