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 com.android.server.audio;
18
19import android.annotation.NonNull;
20import android.app.AppOpsManager;
21import android.content.Context;
22import android.media.AudioAttributes;
23import android.media.AudioFocusInfo;
24import android.media.AudioManager;
25import android.media.AudioSystem;
26import android.media.IAudioFocusDispatcher;
27import android.media.audiopolicy.AudioPolicy;
28import android.media.audiopolicy.IAudioPolicyCallback;
29import android.os.Binder;
30import android.os.Build;
31import android.os.IBinder;
32import android.os.RemoteException;
33import android.util.Log;
34
35import java.io.PrintWriter;
36import java.util.ArrayList;
37import java.util.Date;
38import java.util.HashMap;
39import java.util.Iterator;
40import java.util.Map.Entry;
41import java.util.Set;
42import java.util.Stack;
43import java.text.DateFormat;
44
45/**
46 * @hide
47 *
48 */
49public class MediaFocusControl implements PlayerFocusEnforcer {
50
51    private static final String TAG = "MediaFocusControl";
52    static final boolean DEBUG = false;
53
54    /**
55     * set to true so the framework enforces ducking itself, without communicating to apps
56     * that they lost focus for most use cases.
57     */
58    static final boolean ENFORCE_DUCKING = true;
59    /**
60     * set to true to the framework enforces ducking itself only with apps above a given SDK
61     * target level. Is ignored if ENFORCE_DUCKING is false.
62     */
63    static final boolean ENFORCE_DUCKING_FOR_NEW = true;
64    /**
65     * the SDK level (included) up to which the framework doesn't enforce ducking itself. Is ignored
66     * if ENFORCE_DUCKING_FOR_NEW is false;
67     */
68    // automatic ducking was introduced for Android O
69    static final int DUCKING_IN_APP_SDK_LEVEL = Build.VERSION_CODES.N_MR1;
70    /**
71     * set to true so the framework enforces muting media/game itself when the device is ringing
72     * or in a call.
73     */
74    static final boolean ENFORCE_MUTING_FOR_RING_OR_CALL = true;
75
76    private final Context mContext;
77    private final AppOpsManager mAppOps;
78    private PlayerFocusEnforcer mFocusEnforcer; // never null
79
80    private boolean mRingOrCallActive = false;
81
82    protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) {
83        mContext = cntxt;
84        mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
85        mFocusEnforcer = pfe;
86    }
87
88    protected void dump(PrintWriter pw) {
89        pw.println("\nMediaFocusControl dump time: "
90                + DateFormat.getTimeInstance().format(new Date()));
91        dumpFocusStack(pw);
92    }
93
94    //=================================================================
95    // PlayerFocusEnforcer implementation
96    @Override
97    public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
98        return mFocusEnforcer.duckPlayers(winner, loser);
99    }
100
101    @Override
102    public void unduckPlayers(FocusRequester winner) {
103        mFocusEnforcer.unduckPlayers(winner);
104    }
105
106    @Override
107    public void mutePlayersForCall(int[] usagesToMute) {
108        mFocusEnforcer.mutePlayersForCall(usagesToMute);
109    }
110
111    @Override
112    public void unmutePlayersForCall() {
113        mFocusEnforcer.unmutePlayersForCall();
114    }
115
116    //==========================================================================================
117    // AudioFocus
118    //==========================================================================================
119
120    private final static Object mAudioFocusLock = new Object();
121
122    /**
123     * Discard the current audio focus owner.
124     * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
125     * focus), remove it from the stack, and clear the remote control display.
126     */
127    protected void discardAudioFocusOwner() {
128        synchronized(mAudioFocusLock) {
129            if (!mFocusStack.empty()) {
130                // notify the current focus owner it lost focus after removing it from stack
131                final FocusRequester exFocusOwner = mFocusStack.pop();
132                exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null);
133                exFocusOwner.release();
134            }
135        }
136    }
137
138    /**
139     * Called synchronized on mAudioFocusLock
140     */
141    private void notifyTopOfAudioFocusStack() {
142        // notify the top of the stack it gained focus
143        if (!mFocusStack.empty()) {
144            if (canReassignAudioFocus()) {
145                mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
146            }
147        }
148    }
149
150    /**
151     * Focus is requested, propagate the associated loss throughout the stack.
152     * @param focusGain the new focus gain that will later be added at the top of the stack
153     */
154    private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr) {
155        // going through the audio focus stack to signal new focus, traversing order doesn't
156        // matter as all entries respond to the same external focus gain
157        Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
158        while(stackIterator.hasNext()) {
159            stackIterator.next().handleExternalFocusGain(focusGain, fr);
160        }
161    }
162
163    private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
164
165    /**
166     * Helper function:
167     * Display in the log the current entries in the audio focus stack
168     */
169    private void dumpFocusStack(PrintWriter pw) {
170        pw.println("\nAudio Focus stack entries (last is top of stack):");
171        synchronized(mAudioFocusLock) {
172            Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
173            while(stackIterator.hasNext()) {
174                stackIterator.next().dump(pw);
175            }
176            pw.println("\n");
177            if (mFocusPolicy == null) {
178                pw.println("No external focus policy\n");
179            } else {
180                pw.println("External focus policy: "+ mFocusPolicy + ", focus owners:\n");
181                dumpExtFocusPolicyFocusOwners(pw);
182            }
183        }
184        pw.println("\n");
185        pw.println(" Notify on duck:  " + mNotifyFocusOwnerOnDuck + "\n");
186        pw.println(" In ring or call: " + mRingOrCallActive + "\n");
187    }
188
189    /**
190     * Helper function:
191     * Called synchronized on mAudioFocusLock
192     * Remove a focus listener from the focus stack.
193     * @param clientToRemove the focus listener
194     * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
195     *   focus, notify the next item in the stack it gained focus.
196     */
197    private void removeFocusStackEntry(String clientToRemove, boolean signal,
198            boolean notifyFocusFollowers) {
199        // is the current top of the focus stack abandoning focus? (because of request, not death)
200        if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove))
201        {
202            //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
203            FocusRequester fr = mFocusStack.pop();
204            fr.release();
205            if (notifyFocusFollowers) {
206                final AudioFocusInfo afi = fr.toAudioFocusInfo();
207                afi.clearLossReceived();
208                notifyExtPolicyFocusLoss_syncAf(afi, false);
209            }
210            if (signal) {
211                // notify the new top of the stack it gained focus
212                notifyTopOfAudioFocusStack();
213            }
214        } else {
215            // focus is abandoned by a client that's not at the top of the stack,
216            // no need to update focus.
217            // (using an iterator on the stack so we can safely remove an entry after having
218            //  evaluated it, traversal order doesn't matter here)
219            Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
220            while(stackIterator.hasNext()) {
221                FocusRequester fr = stackIterator.next();
222                if(fr.hasSameClient(clientToRemove)) {
223                    Log.i(TAG, "AudioFocus  removeFocusStackEntry(): removing entry for "
224                            + clientToRemove);
225                    stackIterator.remove();
226                    // stack entry not used anymore, clear references
227                    fr.release();
228                }
229            }
230        }
231    }
232
233    /**
234     * Helper function:
235     * Called synchronized on mAudioFocusLock
236     * Remove focus listeners from the focus stack for a particular client when it has died.
237     */
238    private void removeFocusStackEntryOnDeath(IBinder cb) {
239        // is the owner of the audio focus part of the client to remove?
240        boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
241                mFocusStack.peek().hasSameBinder(cb);
242        // (using an iterator on the stack so we can safely remove an entry after having
243        //  evaluated it, traversal order doesn't matter here)
244        Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
245        while(stackIterator.hasNext()) {
246            FocusRequester fr = stackIterator.next();
247            if(fr.hasSameBinder(cb)) {
248                Log.i(TAG, "AudioFocus  removeFocusStackEntryOnDeath(): removing entry for " + cb);
249                stackIterator.remove();
250                // stack entry not used anymore, clear references
251                fr.release();
252            }
253        }
254        if (isTopOfStackForClientToRemove) {
255            // we removed an entry at the top of the stack:
256            //  notify the new top of the stack it gained focus.
257            notifyTopOfAudioFocusStack();
258        }
259    }
260
261    /**
262     * Helper function for external focus policy:
263     * Called synchronized on mAudioFocusLock
264     * Remove focus listeners from the list of potential focus owners for a particular client when
265     * it has died.
266     */
267    private void removeFocusEntryForExtPolicy(IBinder cb) {
268        if (mFocusOwnersForFocusPolicy.isEmpty()) {
269            return;
270        }
271        boolean released = false;
272        final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
273        final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
274        while (ownerIterator.hasNext()) {
275            final Entry<String, FocusRequester> owner = ownerIterator.next();
276            final FocusRequester fr = owner.getValue();
277            if (fr.hasSameBinder(cb)) {
278                ownerIterator.remove();
279                fr.release();
280                notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo());
281                break;
282            }
283        }
284    }
285
286    /**
287     * Helper function:
288     * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
289     * The implementation guarantees that a state where focus cannot be immediately reassigned
290     * implies that an "locked" focus owner is at the top of the focus stack.
291     * Modifications to the implementation that break this assumption will cause focus requests to
292     * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag.
293     */
294    private boolean canReassignAudioFocus() {
295        // focus requests are rejected during a phone call or when the phone is ringing
296        // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
297        if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) {
298            return false;
299        }
300        return true;
301    }
302
303    private boolean isLockedFocusOwner(FocusRequester fr) {
304        return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner());
305    }
306
307    /**
308     * Helper function
309     * Pre-conditions: focus stack is not empty, there is one or more locked focus owner
310     *                 at the top of the focus stack
311     * Push the focus requester onto the audio focus stack at the first position immediately
312     * following the locked focus owners.
313     * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
314     *     {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
315     */
316    private int pushBelowLockedFocusOwners(FocusRequester nfr) {
317        int lastLockedFocusOwnerIndex = mFocusStack.size();
318        for (int index = mFocusStack.size()-1; index >= 0; index--) {
319            if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
320                lastLockedFocusOwnerIndex = index;
321            }
322        }
323        if (lastLockedFocusOwnerIndex == mFocusStack.size()) {
324            // this should not happen, but handle it and log an error
325            Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()",
326                    new Exception());
327            // no exclusive owner, push at top of stack, focus is granted, propagate change
328            propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr);
329            mFocusStack.push(nfr);
330            return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
331        } else {
332            mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
333            return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
334        }
335    }
336
337    /**
338     * Inner class to monitor audio focus client deaths, and remove them from the audio focus
339     * stack if necessary.
340     */
341    protected class AudioFocusDeathHandler implements IBinder.DeathRecipient {
342        private IBinder mCb; // To be notified of client's death
343
344        AudioFocusDeathHandler(IBinder cb) {
345            mCb = cb;
346        }
347
348        public void binderDied() {
349            synchronized(mAudioFocusLock) {
350                if (mFocusPolicy != null) {
351                    removeFocusEntryForExtPolicy(mCb);
352                } else {
353                    removeFocusStackEntryOnDeath(mCb);
354                }
355            }
356        }
357    }
358
359    /**
360     * Indicates whether to notify an audio focus owner when it loses focus
361     * with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK} if it will only duck.
362     * This variable being false indicates an AudioPolicy has been registered and has signaled
363     * it will handle audio ducking.
364     */
365    private boolean mNotifyFocusOwnerOnDuck = true;
366
367    protected void setDuckingInExtPolicyAvailable(boolean available) {
368        mNotifyFocusOwnerOnDuck = !available;
369    }
370
371    boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; }
372
373    private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>();
374
375    void addFocusFollower(IAudioPolicyCallback ff) {
376        if (ff == null) {
377            return;
378        }
379        synchronized(mAudioFocusLock) {
380            boolean found = false;
381            for (IAudioPolicyCallback pcb : mFocusFollowers) {
382                if (pcb.asBinder().equals(ff.asBinder())) {
383                    found = true;
384                    break;
385                }
386            }
387            if (found) {
388                return;
389            } else {
390                mFocusFollowers.add(ff);
391                notifyExtPolicyCurrentFocusAsync(ff);
392            }
393        }
394    }
395
396    void removeFocusFollower(IAudioPolicyCallback ff) {
397        if (ff == null) {
398            return;
399        }
400        synchronized(mAudioFocusLock) {
401            for (IAudioPolicyCallback pcb : mFocusFollowers) {
402                if (pcb.asBinder().equals(ff.asBinder())) {
403                    mFocusFollowers.remove(pcb);
404                    break;
405                }
406            }
407        }
408    }
409
410    private IAudioPolicyCallback mFocusPolicy = null;
411
412    // Since we don't have a stack of focus owners when using an external focus policy, we keep
413    // track of all the focus requesters in this map, with their clientId as the key. This is
414    // used both for focus dispatch and death handling
415    private HashMap<String, FocusRequester> mFocusOwnersForFocusPolicy =
416            new HashMap<String, FocusRequester>();
417
418    void setFocusPolicy(IAudioPolicyCallback policy) {
419        if (policy == null) {
420            return;
421        }
422        synchronized (mAudioFocusLock) {
423            mFocusPolicy = policy;
424        }
425    }
426
427    void unsetFocusPolicy(IAudioPolicyCallback policy) {
428        if (policy == null) {
429            return;
430        }
431        synchronized (mAudioFocusLock) {
432            if (mFocusPolicy == policy) {
433                mFocusPolicy = null;
434            }
435        }
436    }
437
438    /**
439     * @param pcb non null
440     */
441    void notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb) {
442        final IAudioPolicyCallback pcb2 = pcb;
443        final Thread thread = new Thread() {
444            @Override
445            public void run() {
446                synchronized(mAudioFocusLock) {
447                    if (mFocusStack.isEmpty()) {
448                        return;
449                    }
450                    try {
451                        pcb2.notifyAudioFocusGrant(mFocusStack.peek().toAudioFocusInfo(),
452                                // top of focus stack always has focus
453                                AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
454                    } catch (RemoteException e) {
455                        Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
456                                + pcb2.asBinder(), e);
457                    }
458                }
459            }
460        };
461        thread.start();
462    }
463
464    /**
465     * Called synchronized on mAudioFocusLock
466     */
467    void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) {
468        for (IAudioPolicyCallback pcb : mFocusFollowers) {
469            try {
470                // oneway
471                pcb.notifyAudioFocusGrant(afi, requestResult);
472            } catch (RemoteException e) {
473                Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
474                        + pcb.asBinder(), e);
475            }
476        }
477    }
478
479    /**
480     * Called synchronized on mAudioFocusLock
481     */
482    void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) {
483        for (IAudioPolicyCallback pcb : mFocusFollowers) {
484            try {
485                // oneway
486                pcb.notifyAudioFocusLoss(afi, wasDispatched);
487            } catch (RemoteException e) {
488                Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback "
489                        + pcb.asBinder(), e);
490            }
491        }
492    }
493
494    /**
495     * Called synchronized on mAudioFocusLock
496     * @param afi
497     * @param requestResult
498     * @return true if the external audio focus policy (if any) is handling the focus request
499     */
500    boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, int requestResult,
501            IAudioFocusDispatcher fd, IBinder cb) {
502        if (mFocusPolicy == null) {
503            return false;
504        }
505        if (DEBUG) {
506            Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId()
507            + " dispatcher=" + fd);
508        }
509        final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
510        if (existingFr != null) {
511            if (!existingFr.hasSameDispatcher(fd)) {
512                existingFr.release();
513                final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb);
514                mFocusOwnersForFocusPolicy.put(afi.getClientId(),
515                        new FocusRequester(afi, fd, cb, hdlr, this));
516            }
517        } else if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
518                 || requestResult == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
519            // new focus (future) focus owner to keep track of
520            final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb);
521            mFocusOwnersForFocusPolicy.put(afi.getClientId(),
522                    new FocusRequester(afi, fd, cb, hdlr, this));
523        }
524        try {
525            //oneway
526            mFocusPolicy.notifyAudioFocusRequest(afi, requestResult);
527        } catch (RemoteException e) {
528            Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback "
529                    + mFocusPolicy.asBinder(), e);
530        }
531        return true;
532    }
533
534    /**
535     * Called synchronized on mAudioFocusLock
536     * @param afi
537     * @param requestResult
538     * @return true if the external audio focus policy (if any) is handling the focus request
539     */
540    boolean notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi) {
541        if (mFocusPolicy == null) {
542            return false;
543        }
544        final FocusRequester fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
545        if (fr != null) {
546            fr.release();
547        }
548        try {
549            //oneway
550            mFocusPolicy.notifyAudioFocusAbandon(afi);
551        } catch (RemoteException e) {
552            Log.e(TAG, "Can't call notifyAudioFocusAbandon() on IAudioPolicyCallback "
553                    + mFocusPolicy.asBinder(), e);
554        }
555        return true;
556    }
557
558    /** see AudioManager.dispatchFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) */
559    int dispatchFocusChange(AudioFocusInfo afi, int focusChange) {
560        if (DEBUG) {
561            Log.v(TAG, "dispatchFocusChange " + focusChange + " to afi client="
562                    + afi.getClientId());
563        }
564        synchronized (mAudioFocusLock) {
565            if (mFocusPolicy == null) {
566                if (DEBUG) { Log.v(TAG, "> failed: no focus policy" ); }
567                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
568            }
569            final FocusRequester fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
570            if (fr == null) {
571                if (DEBUG) { Log.v(TAG, "> failed: no such focus requester known" ); }
572                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
573            }
574            return fr.dispatchFocusChange(focusChange);
575        }
576    }
577
578    private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) {
579        final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
580        final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
581        while (ownerIterator.hasNext()) {
582            final Entry<String, FocusRequester> owner = ownerIterator.next();
583            final FocusRequester fr = owner.getValue();
584            fr.dump(pw);
585        }
586    }
587
588    protected int getCurrentAudioFocus() {
589        synchronized(mAudioFocusLock) {
590            if (mFocusStack.empty()) {
591                return AudioManager.AUDIOFOCUS_NONE;
592            } else {
593                return mFocusStack.peek().getGainRequest();
594            }
595        }
596    }
597
598    /**
599     * Delay after entering ringing or call mode after which the framework will mute streams
600     * that are still playing.
601     */
602    private static final int RING_CALL_MUTING_ENFORCEMENT_DELAY_MS = 100;
603
604    /**
605     * Usages to mute when the device rings or is in a call
606     */
607    private final static int[] USAGES_TO_MUTE_IN_RING_OR_CALL =
608        { AudioAttributes.USAGE_MEDIA, AudioAttributes.USAGE_GAME };
609
610    /**
611     * Return the volume ramp time expected before playback with the given AudioAttributes would
612     * start after gaining audio focus.
613     * @param attr attributes of the sound about to start playing
614     * @return time in ms
615     */
616    protected static int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
617        switch (attr.getUsage()) {
618            case AudioAttributes.USAGE_MEDIA:
619            case AudioAttributes.USAGE_GAME:
620                return 1000;
621            case AudioAttributes.USAGE_ALARM:
622            case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
623            case AudioAttributes.USAGE_ASSISTANT:
624            case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY:
625            case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
626                return 700;
627            case AudioAttributes.USAGE_VOICE_COMMUNICATION:
628            case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING:
629            case AudioAttributes.USAGE_NOTIFICATION:
630            case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
631            case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
632            case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
633            case AudioAttributes.USAGE_NOTIFICATION_EVENT:
634            case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION:
635                return 500;
636            case AudioAttributes.USAGE_UNKNOWN:
637            default:
638                return 0;
639        }
640    }
641
642    /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */
643    protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
644            IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
645            int sdk) {
646        Log.i(TAG, " AudioFocus  requestAudioFocus() from uid/pid " + Binder.getCallingUid()
647                + "/" + Binder.getCallingPid()
648                + " clientId=" + clientId
649                + " req=" + focusChangeHint
650                + " flags=0x" + Integer.toHexString(flags));
651        // we need a valid binder callback for clients
652        if (!cb.pingBinder()) {
653            Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
654            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
655        }
656
657        if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
658                callingPackageName) != AppOpsManager.MODE_ALLOWED) {
659            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
660        }
661
662        synchronized(mAudioFocusLock) {
663            boolean enteringRingOrCall = !mRingOrCallActive
664                    & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
665            if (enteringRingOrCall) { mRingOrCallActive = true; }
666
667            final AudioFocusInfo afiForExtPolicy;
668            if (mFocusPolicy != null) {
669                // construct AudioFocusInfo as it will be communicated to audio focus policy
670                afiForExtPolicy = new AudioFocusInfo(aa, Binder.getCallingUid(),
671                        clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/,
672                        flags, sdk);
673            } else {
674                afiForExtPolicy = null;
675            }
676
677            // handle delayed focus
678            boolean focusGrantDelayed = false;
679            if (!canReassignAudioFocus()) {
680                if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
681                    final int result = AudioManager.AUDIOFOCUS_REQUEST_FAILED;
682                    notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, result, fd, cb);
683                    return result;
684                } else {
685                    // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be
686                    // granted right now, so the requester will be inserted in the focus stack
687                    // to receive focus later
688                    focusGrantDelayed = true;
689                }
690            }
691
692            // external focus policy: delay request for focus gain?
693            final int resultWithExtPolicy = AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
694            if (notifyExtFocusPolicyFocusRequest_syncAf(
695                    afiForExtPolicy, resultWithExtPolicy, fd, cb)) {
696                // stop handling focus request here as it is handled by external audio focus policy
697                return resultWithExtPolicy;
698            }
699
700            // handle the potential premature death of the new holder of the focus
701            // (premature death == death before abandoning focus)
702            // Register for client death notification
703            AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
704
705            try {
706                cb.linkToDeath(afdh, 0);
707            } catch (RemoteException e) {
708                // client has already died!
709                Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
710                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
711            }
712
713            if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
714                // if focus is already owned by this client and the reason for acquiring the focus
715                // hasn't changed, don't do anything
716                final FocusRequester fr = mFocusStack.peek();
717                if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {
718                    // unlink death handler so it can be gc'ed.
719                    // linkToDeath() creates a JNI global reference preventing collection.
720                    cb.unlinkToDeath(afdh, 0);
721                    notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(),
722                            AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
723                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
724                }
725                // the reason for the audio focus request has changed: remove the current top of
726                // stack and respond as if we had a new focus owner
727                if (!focusGrantDelayed) {
728                    mFocusStack.pop();
729                    // the entry that was "popped" is the same that was "peeked" above
730                    fr.release();
731                }
732            }
733
734            // focus requester might already be somewhere below in the stack, remove it
735            removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
736
737            final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
738                    clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk);
739            if (focusGrantDelayed) {
740                // focusGrantDelayed being true implies we can't reassign focus right now
741                // which implies the focus stack is not empty.
742                final int requestResult = pushBelowLockedFocusOwners(nfr);
743                if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
744                    notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);
745                }
746                return requestResult;
747            } else {
748                // propagate the focus change through the stack
749                if (!mFocusStack.empty()) {
750                    propagateFocusLossFromGain_syncAf(focusChangeHint, nfr);
751                }
752
753                // push focus requester at the top of the audio focus stack
754                mFocusStack.push(nfr);
755                nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
756            }
757            notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
758                    AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
759
760            if (ENFORCE_MUTING_FOR_RING_OR_CALL & enteringRingOrCall) {
761                runAudioCheckerForRingOrCallAsync(true/*enteringRingOrCall*/);
762            }
763        }//synchronized(mAudioFocusLock)
764
765        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
766    }
767
768    /**
769     * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
770     * */
771    protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa,
772            String callingPackageName) {
773        // AudioAttributes are currently ignored, to be used for zones
774        Log.i(TAG, " AudioFocus  abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
775                + "/" + Binder.getCallingPid()
776                + " clientId=" + clientId);
777        try {
778            // this will take care of notifying the new focus owner if needed
779            synchronized(mAudioFocusLock) {
780                // external focus policy?
781                if (mFocusPolicy != null) {
782                    final AudioFocusInfo afi = new AudioFocusInfo(aa, Binder.getCallingUid(),
783                            clientId, callingPackageName, 0 /*gainRequest*/, 0 /*lossReceived*/,
784                            0 /*flags*/, 0 /* sdk n/a here*/);
785                    if (notifyExtFocusPolicyFocusAbandon_syncAf(afi)) {
786                        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
787                    }
788                }
789
790                boolean exitingRingOrCall = mRingOrCallActive
791                        & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
792                if (exitingRingOrCall) { mRingOrCallActive = false; }
793
794                removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/);
795
796                if (ENFORCE_MUTING_FOR_RING_OR_CALL & exitingRingOrCall) {
797                    runAudioCheckerForRingOrCallAsync(false/*enteringRingOrCall*/);
798                }
799            }
800        } catch (java.util.ConcurrentModificationException cme) {
801            // Catching this exception here is temporary. It is here just to prevent
802            // a crash seen when the "Silent" notification is played. This is believed to be fixed
803            // but this try catch block is left just to be safe.
804            Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
805            cme.printStackTrace();
806        }
807
808        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
809    }
810
811
812    protected void unregisterAudioFocusClient(String clientId) {
813        synchronized(mAudioFocusLock) {
814            removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/);
815        }
816    }
817
818    private void runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall) {
819        new Thread() {
820            public void run() {
821                if (enteringRingOrCall) {
822                    try {
823                        Thread.sleep(RING_CALL_MUTING_ENFORCEMENT_DELAY_MS);
824                    } catch (InterruptedException e) {
825                        e.printStackTrace();
826                    }
827                }
828                synchronized (mAudioFocusLock) {
829                    // since the new thread starting running the state could have changed, so
830                    // we need to check again mRingOrCallActive, not enteringRingOrCall
831                    if (mRingOrCallActive) {
832                        mFocusEnforcer.mutePlayersForCall(USAGES_TO_MUTE_IN_RING_OR_CALL);
833                    } else {
834                        mFocusEnforcer.unmutePlayersForCall();
835                    }
836                }
837            }
838        }.start();
839    }
840}
841