• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.audio;
18 
19 import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.app.AppOpsManager;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.media.AudioAttributes;
27 import android.media.AudioFocusInfo;
28 import android.media.AudioManager;
29 import android.media.AudioSystem;
30 import android.media.IAudioFocusDispatcher;
31 import android.media.MediaMetrics;
32 import android.media.audiopolicy.AudioPolicy;
33 import android.media.audiopolicy.IAudioPolicyCallback;
34 import android.os.Binder;
35 import android.os.Build;
36 import android.os.Handler;
37 import android.os.HandlerThread;
38 import android.os.IBinder;
39 import android.os.Message;
40 import android.os.RemoteException;
41 import android.provider.Settings;
42 import android.util.Log;
43 
44 import com.android.internal.annotations.GuardedBy;
45 import com.android.server.utils.EventLogger;
46 
47 import java.io.PrintWriter;
48 import java.text.DateFormat;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Date;
52 import java.util.HashMap;
53 import java.util.Iterator;
54 import java.util.LinkedList;
55 import java.util.List;
56 import java.util.Map.Entry;
57 import java.util.Set;
58 import java.util.Stack;
59 
60 /**
61  * @hide
62  *
63  */
64 public class MediaFocusControl implements PlayerFocusEnforcer {
65 
66     private static final String TAG = "MediaFocusControl";
67     static final boolean DEBUG = false;
68 
69     /**
70      * set to true so the framework enforces ducking itself, without communicating to apps
71      * that they lost focus for most use cases.
72      */
73     static final boolean ENFORCE_DUCKING = true;
74     /**
75      * set to true to the framework enforces ducking itself only with apps above a given SDK
76      * target level. Is ignored if ENFORCE_DUCKING is false.
77      */
78     static final boolean ENFORCE_DUCKING_FOR_NEW = true;
79     /**
80      * the SDK level (included) up to which the framework doesn't enforce ducking itself. Is ignored
81      * if ENFORCE_DUCKING_FOR_NEW is false;
82      */
83     // automatic ducking was introduced for Android O
84     static final int DUCKING_IN_APP_SDK_LEVEL = Build.VERSION_CODES.N_MR1;
85     /**
86      * set to true so the framework enforces muting media/game itself when the device is ringing
87      * or in a call.
88      */
89     static final boolean ENFORCE_MUTING_FOR_RING_OR_CALL = true;
90 
91     /**
92      * set to true so the framework enforces fading out apps that lose audio focus in a
93      * non-transient way.
94      */
95     static final boolean ENFORCE_FADEOUT_FOR_FOCUS_LOSS = true;
96 
97     private final Context mContext;
98     private final AppOpsManager mAppOps;
99     private final @NonNull PlayerFocusEnforcer mFocusEnforcer;
100     private boolean mMultiAudioFocusEnabled = false;
101 
102     private boolean mRingOrCallActive = false;
103 
104     private final Object mExtFocusChangeLock = new Object();
105     @GuardedBy("mExtFocusChangeLock")
106     private long mExtFocusChangeCounter;
107 
MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe)108     protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) {
109         mContext = cntxt;
110         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
111         mFocusEnforcer = pfe;
112         final ContentResolver cr = mContext.getContentResolver();
113         mMultiAudioFocusEnabled = Settings.System.getIntForUser(cr,
114                 Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0, cr.getUserId()) != 0;
115         initFocusThreading();
116     }
117 
dump(PrintWriter pw)118     protected void dump(PrintWriter pw) {
119         pw.println("\nMediaFocusControl dump time: "
120                 + DateFormat.getTimeInstance().format(new Date()));
121         dumpFocusStack(pw);
122         pw.println("\n");
123         // log
124         mEventLogger.dump(pw);
125         dumpMultiAudioFocus(pw);
126     }
127 
128     /**
129      * Test method to return the duration of the fade out applied on the players of a focus loser
130      * @return the fade out duration in ms
131      */
getFocusFadeOutDurationForTest()132     public long getFocusFadeOutDurationForTest() {
133         return getFadeOutDurationMillis(
134                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build());
135     }
136 
137     /**
138      * Test method to return the length of time after a fade out before the focus loser is unmuted
139      * (and is faded back in).
140      * @return the time gap after a fade out completion on focus loss, and fade in start in ms
141      */
getFocusUnmuteDelayAfterFadeOutForTest()142     public long getFocusUnmuteDelayAfterFadeOutForTest() {
143         return getFadeInDelayForOffendersMillis(
144                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build());
145     }
146 
147     //=================================================================
148     // PlayerFocusEnforcer implementation
149     @Override
duckPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser, boolean forceDuck)150     public boolean duckPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser,
151                                boolean forceDuck) {
152         return mFocusEnforcer.duckPlayers(winner, loser, forceDuck);
153     }
154 
155     @Override
restoreVShapedPlayers(@onNull FocusRequester winner)156     public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
157         mFocusEnforcer.restoreVShapedPlayers(winner);
158         // remove scheduled events to unfade out offending players (if any) corresponding to
159         // this uid, as we're removing any effects of muting/ducking/fade out now
160         mFocusHandler.removeEqualMessages(MSL_L_FORGET_UID,
161                 new ForgetFadeUidInfo(winner.getClientUid()));
162 
163     }
164 
165     @Override
mutePlayersForCall(int[] usagesToMute)166     public void mutePlayersForCall(int[] usagesToMute) {
167         mFocusEnforcer.mutePlayersForCall(usagesToMute);
168     }
169 
170     @Override
unmutePlayersForCall()171     public void unmutePlayersForCall() {
172         mFocusEnforcer.unmutePlayersForCall();
173     }
174 
175     @Override
fadeOutPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser)176     public boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser) {
177         return mFocusEnforcer.fadeOutPlayers(winner, loser);
178     }
179 
180     @Override
forgetUid(int uid)181     public void forgetUid(int uid) {
182         mFocusEnforcer.forgetUid(uid);
183     }
184 
185     @Override
getFadeOutDurationMillis(@onNull AudioAttributes aa)186     public long getFadeOutDurationMillis(@NonNull AudioAttributes aa) {
187         if (aa == null) {
188             return 0;
189         }
190         return mFocusEnforcer.getFadeOutDurationMillis(aa);
191     }
192 
193     @Override
getFadeInDelayForOffendersMillis(@onNull AudioAttributes aa)194     public long getFadeInDelayForOffendersMillis(@NonNull AudioAttributes aa) {
195         if (aa == null) {
196             return 0;
197         }
198         return mFocusEnforcer.getFadeInDelayForOffendersMillis(aa);
199     }
200 
201     @Override
shouldEnforceFade()202     public boolean shouldEnforceFade() {
203         if (!enableFadeManagerConfiguration()) {
204             return ENFORCE_FADEOUT_FOR_FOCUS_LOSS;
205         }
206 
207         return mFocusEnforcer.shouldEnforceFade();
208     }
209     //==========================================================================================
210     // AudioFocus
211     //==========================================================================================
212 
213     private final static Object mAudioFocusLock = new Object();
214 
215     /**
216      * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process.
217      */
218     private static final int MAX_STACK_SIZE = 100;
219 
220     private static final EventLogger
221             mEventLogger = new EventLogger(50,
222             "focus commands as seen by MediaFocusControl");
223 
224     private static final String mMetricsId = MediaMetrics.Name.AUDIO_FOCUS;
225 
noFocusForSuspendedApp(@onNull String packageName, int uid)226     /*package*/ void noFocusForSuspendedApp(@NonNull String packageName, int uid) {
227         synchronized (mAudioFocusLock) {
228             final Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
229             List<String> clientsToRemove = new ArrayList<>();
230             while (stackIterator.hasNext()) {
231                 final FocusRequester focusOwner = stackIterator.next();
232                 if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) {
233                     clientsToRemove.add(focusOwner.getClientId());
234                     // make the suspended app lose focus through its focus listener (if any)
235                     focusOwner.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS, "app suspension");
236                 }
237             }
238             for (String clientToRemove : clientsToRemove) {
239                 // update the stack but don't signal the change.
240                 removeFocusStackEntry(clientToRemove, false, true);
241             }
242         }
243     }
244 
hasAudioFocusUsers()245     /*package*/ boolean hasAudioFocusUsers() {
246         synchronized (mAudioFocusLock) {
247             return !mFocusStack.empty();
248         }
249     }
250 
251     /**
252      * Discard the current audio focus owner (unless the user is considered {@link
253      * FocusRequester#isAlwaysVisibleUser() always visible)}.
254      * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
255      * focus), remove it from the stack, and clear the remote control display.
256      * @return whether the current audio focus owner was discarded (including if there was none);
257      *         returns false if it was purposefully kept
258      */
maybeDiscardAudioFocusOwner()259     protected boolean maybeDiscardAudioFocusOwner() {
260         synchronized(mAudioFocusLock) {
261             if (!mFocusStack.empty()) {
262                 final FocusRequester exFocusOwner = mFocusStack.peek();
263                 if (!exFocusOwner.isAlwaysVisibleUser()) {
264                     mFocusStack.pop();
265                     exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null,
266                             false /*forceDuck*/);
267                     exFocusOwner.release();
268                     return true;
269                 } else {
270                     return false;
271                 }
272             }
273         }
274         return true;
275     }
276 
277     /**
278      * Like {@link #sendFocusLoss(AudioFocusInfo)} but if the loser was at the top of stack,
279      * make the next entry gain focus with {@link AudioManager#AUDIOFOCUS_GAIN}.
280      * @param focusInfo the focus owner to discard
281      * @see AudioPolicy#sendFocusLossAndUpdate(AudioFocusInfo)
282      */
sendFocusLossAndUpdate(@onNull AudioFocusInfo focusInfo)283     protected void sendFocusLossAndUpdate(@NonNull AudioFocusInfo focusInfo) {
284         synchronized (mAudioFocusLock) {
285             if (mFocusStack.isEmpty()) {
286                 return;
287             }
288             final FocusRequester currentFocusOwner = mFocusStack.peek();
289             if (currentFocusOwner.toAudioFocusInfo().equals(focusInfo)) {
290                 // focus loss is for the top of the stack
291                 currentFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null,
292                             false /*forceDuck*/);
293                 currentFocusOwner.release();
294 
295                 mFocusStack.pop();
296                 // is there a new focus owner?
297                 if (!mFocusStack.isEmpty()) {
298                     mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
299                 }
300             } else {
301                 // focus loss if for another entry that's not at the top of the stack,
302                 // just remove it from the stack and make it lose focus
303                 sendFocusLoss(focusInfo);
304             }
305         }
306     }
307 
308     /**
309      * Return a copy of the focus stack for external consumption (composed of AudioFocusInfo
310      * instead of FocusRequester instances)
311      * @return a SystemApi-friendly version of the focus stack, in the same order (last entry
312      *         is top of focus stack, i.e. latest focus owner)
313      * @see AudioPolicy#getFocusStack()
314      */
getFocusStack()315     @NonNull List<AudioFocusInfo> getFocusStack() {
316         synchronized (mAudioFocusLock) {
317             final ArrayList<AudioFocusInfo> stack = new ArrayList<>(mFocusStack.size());
318             for (FocusRequester fr : mFocusStack) {
319                 stack.add(fr.toAudioFocusInfo());
320             }
321             return stack;
322         }
323     }
324 
325     /**
326      * Return the UID of the focus owner that has focus with exclusive focus gain
327      * @return -1 if nobody has exclusive focus, the UID of the owner otherwise
328      */
getExclusiveFocusOwnerUid()329     protected int getExclusiveFocusOwnerUid() {
330         synchronized (mAudioFocusLock) {
331             if (mFocusStack.empty()) {
332                 return -1;
333             }
334             final FocusRequester owner = mFocusStack.peek();
335             if (owner.getGainRequest() != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) {
336                 return -1;
337             }
338             return owner.getClientUid();
339         }
340     }
341 
342     /**
343      * Send AUDIOFOCUS_LOSS to a specific stack entry.
344      * Note this method is supporting an external API, and is restricted to LOSS in order to
345      * prevent allowing the stack to be in an invalid state (e.g. entry inside stack has focus)
346      * @param focusLoser the stack entry that is exiting the stack through a focus loss
347      * @return false if the focusLoser wasn't found in the stack, true otherwise
348      * @see AudioPolicy#sendFocusLoss(AudioFocusInfo)
349      */
sendFocusLoss(@onNull AudioFocusInfo focusLoser)350     boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) {
351         synchronized (mAudioFocusLock) {
352             FocusRequester loserToRemove = null;
353             for (FocusRequester fr : mFocusStack) {
354                 if (fr.getClientId().equals(focusLoser.getClientId())) {
355                     fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null,
356                             false /*forceDuck*/);
357                     loserToRemove = fr;
358                     break;
359                 }
360             }
361             if (loserToRemove != null) {
362                 mFocusStack.remove(loserToRemove);
363                 loserToRemove.release();
364                 return true;
365             }
366         }
367         return false;
368     }
369 
370     @GuardedBy("mAudioFocusLock")
notifyTopOfAudioFocusStack()371     private void notifyTopOfAudioFocusStack() {
372         // notify the top of the stack it gained focus
373         if (!mFocusStack.empty()) {
374             if (canReassignAudioFocus()) {
375                 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
376             }
377         }
378 
379         if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
380             for (FocusRequester multifr : mMultiAudioFocusList) {
381                 if (isLockedFocusOwner(multifr)) {
382                     multifr.handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
383                 }
384             }
385         }
386     }
387 
388     /**
389      * Focus is requested, propagate the associated loss throughout the stack.
390      * Will also remove entries in the stack that have just received a definitive loss of focus.
391      * @param focusGain the new focus gain that will later be added at the top of the stack
392      */
393     @GuardedBy("mAudioFocusLock")
propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr, boolean forceDuck)394     private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr,
395                                                    boolean forceDuck) {
396         if (DEBUG) {
397             Log.i(TAG, "propagateFocusLossFromGain_syncAf gain:" + focusGain);
398         }
399         final List<String> clientsToRemove = new LinkedList<String>();
400         // going through the audio focus stack to signal new focus, traversing order doesn't
401         // matter as all entries respond to the same external focus gain
402         if (!mFocusStack.empty()) {
403             for (FocusRequester focusLoser : mFocusStack) {
404                 if (DEBUG) {
405                     Log.i(TAG, "propagateFocusLossFromGain_syncAf checking client:"
406                             + focusLoser.getClientId());
407                 }
408                 final boolean isDefinitiveLoss =
409                         focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
410                 if (isDefinitiveLoss) {
411                     clientsToRemove.add(focusLoser.getClientId());
412                 }
413             }
414         } else if (DEBUG) {
415             Log.i(TAG, "propagateFocusLossFromGain_syncAf empty stack");
416         }
417 
418         if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
419             for (FocusRequester multifocusLoser : mMultiAudioFocusList) {
420                 final boolean isDefinitiveLoss =
421                         multifocusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
422                 if (isDefinitiveLoss) {
423                     clientsToRemove.add(multifocusLoser.getClientId());
424                 }
425             }
426         }
427 
428         for (String clientToRemove : clientsToRemove) {
429             removeFocusStackEntry(clientToRemove, false /*signal*/,
430                     true /*notifyFocusFollowers*/);
431         }
432     }
433 
434     private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
435 
436     ArrayList<FocusRequester> mMultiAudioFocusList = new ArrayList<FocusRequester>();
437 
438     /**
439      * Helper function:
440      * Display in the log the current entries in the audio focus stack
441      */
dumpFocusStack(PrintWriter pw)442     private void dumpFocusStack(PrintWriter pw) {
443         pw.println("\nAudio Focus stack entries (last is top of stack):");
444         synchronized(mAudioFocusLock) {
445             Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
446             while(stackIterator.hasNext()) {
447                 stackIterator.next().dump(pw);
448             }
449             pw.println("\n");
450             if (mFocusPolicy == null) {
451                 pw.println("No external focus policy\n");
452             } else {
453                 pw.println("External focus policy: "+ mFocusPolicy + ", focus owners:\n");
454                 dumpExtFocusPolicyFocusOwners(pw);
455             }
456         }
457         pw.println("\n");
458         pw.println(" Notify on duck:  " + mNotifyFocusOwnerOnDuck + "\n");
459         pw.println(" In ring or call: " + mRingOrCallActive + "\n");
460     }
461 
462     /**
463      * Remove a focus listener from the focus stack.
464      * @param clientToRemove the focus listener
465      * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
466      *   focus, notify the next item in the stack it gained focus.
467      */
468     @GuardedBy("mAudioFocusLock")
removeFocusStackEntry(String clientToRemove, boolean signal, boolean notifyFocusFollowers)469     private void removeFocusStackEntry(String clientToRemove, boolean signal,
470             boolean notifyFocusFollowers) {
471         if (DEBUG) {
472             Log.i(TAG, "removeFocusStackEntry client:" + clientToRemove);
473         }
474         AudioFocusInfo abandonSource = null;
475         // is the current top of the focus stack abandoning focus? (because of request, not death)
476         if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove))
477         {
478             //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
479             FocusRequester fr = mFocusStack.pop();
480             fr.maybeRelease();
481             if (notifyFocusFollowers) {
482                 abandonSource = fr.toAudioFocusInfo();
483             }
484             if (signal) {
485                 // notify the new top of the stack it gained focus
486                 notifyTopOfAudioFocusStack();
487             }
488         } else {
489             // focus is abandoned by a client that's not at the top of the stack,
490             // no need to update focus.
491             // (using an iterator on the stack so we can safely remove an entry after having
492             //  evaluated it, traversal order doesn't matter here)
493             Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
494             while(stackIterator.hasNext()) {
495                 FocusRequester fr = stackIterator.next();
496                 if(fr.hasSameClient(clientToRemove)) {
497                     Log.i(TAG, "AudioFocus  removeFocusStackEntry(): removing entry for "
498                             + clientToRemove);
499                     stackIterator.remove();
500                     if (notifyFocusFollowers) {
501                         abandonSource = fr.toAudioFocusInfo();
502                     }
503                     // stack entry not used anymore, clear references
504                     fr.maybeRelease();
505                 }
506             }
507         }
508         // focus followers still want to know focus was abandoned, handled as a loss
509         if (abandonSource != null) {
510             abandonSource.clearLossReceived();
511             notifyExtPolicyFocusLoss_syncAf(abandonSource, false);
512         }
513 
514         if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
515             Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator();
516             while (listIterator.hasNext()) {
517                 FocusRequester fr = listIterator.next();
518                 if (fr.hasSameClient(clientToRemove)) {
519                     listIterator.remove();
520                     fr.release();
521                 }
522             }
523 
524             if (signal) {
525                 // notify the new top of the stack it gained focus
526                 notifyTopOfAudioFocusStack();
527             }
528         }
529     }
530 
531     /**
532      * Remove focus listeners from the focus stack for a particular client when it has died.
533      */
534     @GuardedBy("mAudioFocusLock")
removeFocusStackEntryOnDeath(IBinder cb)535     private void removeFocusStackEntryOnDeath(IBinder cb) {
536         // is the owner of the audio focus part of the client to remove?
537         boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
538                 mFocusStack.peek().hasSameBinder(cb);
539         // (using an iterator on the stack so we can safely remove an entry after having
540         //  evaluated it, traversal order doesn't matter here)
541         Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
542         while(stackIterator.hasNext()) {
543             FocusRequester fr = stackIterator.next();
544             if(fr.hasSameBinder(cb)) {
545                 Log.i(TAG, "AudioFocus  removeFocusStackEntryOnDeath(): removing entry for " + cb);
546                 mEventLogger.enqueue(new FocusRequester.FocusRequestEvent(fr,  " died"));
547                 notifyExtPolicyFocusLoss_syncAf(fr.toAudioFocusInfo(), false);
548 
549                 stackIterator.remove();
550                 // stack entry not used anymore, clear references
551                 fr.release();
552             }
553         }
554         if (isTopOfStackForClientToRemove) {
555             // we removed an entry at the top of the stack:
556             //  notify the new top of the stack it gained focus.
557             notifyTopOfAudioFocusStack();
558         }
559     }
560 
561     /**
562      * Helper function for external focus policy:
563      * Remove focus listeners from the list of potential focus owners for a particular client when
564      * it has died.
565      */
566     @GuardedBy("mAudioFocusLock")
removeFocusEntryForExtPolicyOnDeath(IBinder cb)567     private void removeFocusEntryForExtPolicyOnDeath(IBinder cb) {
568         if (mFocusOwnersForFocusPolicy.isEmpty()) {
569             return;
570         }
571         boolean released = false;
572         final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
573         final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
574         while (ownerIterator.hasNext()) {
575             final Entry<String, FocusRequester> owner = ownerIterator.next();
576             final FocusRequester fr = owner.getValue();
577             if (fr.hasSameBinder(cb)) {
578                 ownerIterator.remove();
579                 mEventLogger.enqueue(new FocusRequester.FocusRequestEvent(fr, "died"));
580                 fr.release();
581                 notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo());
582                 break;
583             }
584         }
585     }
586 
587     /**
588      * Helper function:
589      * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
590      * The implementation guarantees that a state where focus cannot be immediately reassigned
591      * implies that an "locked" focus owner is at the top of the focus stack.
592      * Modifications to the implementation that break this assumption will cause focus requests to
593      * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag.
594      */
canReassignAudioFocus()595     private boolean canReassignAudioFocus() {
596         // focus requests are rejected during a phone call or when the phone is ringing
597         // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
598         if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) {
599             return false;
600         }
601         return true;
602     }
603 
isLockedFocusOwner(FocusRequester fr)604     private boolean isLockedFocusOwner(FocusRequester fr) {
605         return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner());
606     }
607 
608     /**
609      * Helper function
610      * Pre-conditions: focus stack is not empty, there is one or more locked focus owner
611      *                 at the top of the focus stack
612      * Push the focus requester onto the audio focus stack at the first position immediately
613      * following the locked focus owners.
614      * Propagate through the stack the changes that the new (future) focus owner causes.
615      * @param nfr the future focus owner that will gain focus when the locked focus owners are
616      *            removed from the stack
617      * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
618      *     {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
619      */
620     @GuardedBy("mAudioFocusLock")
pushBelowLockedFocusOwnersAndPropagate(FocusRequester nfr)621     private int pushBelowLockedFocusOwnersAndPropagate(FocusRequester nfr) {
622         if (DEBUG) {
623             Log.v(TAG, "pushBelowLockedFocusOwnersAndPropagate client=" + nfr.getClientId());
624         }
625         int lastLockedFocusOwnerIndex = mFocusStack.size();
626         for (int index = mFocusStack.size() - 1; index >= 0; index--) {
627             if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
628                 lastLockedFocusOwnerIndex = index;
629             }
630         }
631         if (lastLockedFocusOwnerIndex == mFocusStack.size()) {
632             // this should not happen, but handle it and log an error
633             Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()",
634                     new Exception());
635             // no exclusive owner, push at top of stack, focus is granted, propagate change
636             propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr, false /*forceDuck*/);
637             mFocusStack.push(nfr);
638             return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
639         }
640 
641         if (DEBUG) {
642             Log.v(TAG, "> lastLockedFocusOwnerIndex=" + lastLockedFocusOwnerIndex);
643         }
644         mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
645 
646         // propagate potential focus loss (and removal from stack) after the newly
647         // inserted FocusRequester (at index lastLockedFocusOwnerIndex-1)
648         final List<String> clientsToRemove = new LinkedList<String>();
649         for (int index = lastLockedFocusOwnerIndex - 1; index >= 0; index--) {
650             final boolean isDefinitiveLoss =
651                     mFocusStack.elementAt(index).handleFocusLossFromGain(
652                             nfr.getGainRequest(), nfr, false /*forceDuck*/);
653             if (isDefinitiveLoss) {
654                 clientsToRemove.add(mFocusStack.elementAt(index).getClientId());
655             }
656         }
657         for (String clientToRemove : clientsToRemove) {
658             if (DEBUG) {
659                 Log.v(TAG, "> removing focus client " + clientToRemove);
660             }
661             removeFocusStackEntry(clientToRemove, false /*signal*/,
662                     true /*notifyFocusFollowers*/);
663         }
664 
665         return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
666     }
667 
668     /**
669      * Inner class to monitor audio focus client deaths, and remove them from the audio focus
670      * stack if necessary.
671      */
672     protected class AudioFocusDeathHandler implements IBinder.DeathRecipient {
673         private IBinder mCb; // To be notified of client's death
674 
AudioFocusDeathHandler(IBinder cb)675         AudioFocusDeathHandler(IBinder cb) {
676             mCb = cb;
677         }
678 
binderDied()679         public void binderDied() {
680             synchronized(mAudioFocusLock) {
681                 if (mFocusPolicy != null) {
682                     removeFocusEntryForExtPolicyOnDeath(mCb);
683                 } else {
684                     removeFocusStackEntryOnDeath(mCb);
685                     if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
686                         Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator();
687                         while (listIterator.hasNext()) {
688                             FocusRequester fr = listIterator.next();
689                             if (fr.hasSameBinder(mCb)) {
690                                 listIterator.remove();
691                                 fr.release();
692                             }
693                         }
694                     }
695                 }
696             }
697         }
698     }
699 
700     /**
701      * Indicates whether to notify an audio focus owner when it loses focus
702      * with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK} if it will only duck.
703      * This variable being false indicates an AudioPolicy has been registered and has signaled
704      * it will handle audio ducking.
705      */
706     private boolean mNotifyFocusOwnerOnDuck = true;
707 
setDuckingInExtPolicyAvailable(boolean available)708     protected void setDuckingInExtPolicyAvailable(boolean available) {
709         mNotifyFocusOwnerOnDuck = !available;
710     }
711 
mustNotifyFocusOwnerOnDuck()712     boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; }
713 
714     private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>();
715 
addFocusFollower(IAudioPolicyCallback ff)716     void addFocusFollower(IAudioPolicyCallback ff) {
717         if (ff == null) {
718             return;
719         }
720         synchronized(mAudioFocusLock) {
721             boolean found = false;
722             for (IAudioPolicyCallback pcb : mFocusFollowers) {
723                 if (pcb.asBinder().equals(ff.asBinder())) {
724                     found = true;
725                     break;
726                 }
727             }
728             if (found) {
729                 return;
730             } else {
731                 mFocusFollowers.add(ff);
732                 notifyExtPolicyCurrentFocusAsync(ff);
733             }
734         }
735     }
736 
removeFocusFollower(IAudioPolicyCallback ff)737     void removeFocusFollower(IAudioPolicyCallback ff) {
738         if (ff == null) {
739             return;
740         }
741         synchronized(mAudioFocusLock) {
742             for (IAudioPolicyCallback pcb : mFocusFollowers) {
743                 if (pcb.asBinder().equals(ff.asBinder())) {
744                     mFocusFollowers.remove(pcb);
745                     break;
746                 }
747             }
748         }
749     }
750 
751     /** The current audio focus policy */
752     @GuardedBy("mAudioFocusLock")
753     @Nullable private IAudioPolicyCallback mFocusPolicy = null;
754     /**
755      * The audio focus policy that was registered before a test focus policy was registered
756      * during a test
757      */
758     @GuardedBy("mAudioFocusLock")
759     @Nullable private IAudioPolicyCallback mPreviousFocusPolicy = null;
760 
761     // Since we don't have a stack of focus owners when using an external focus policy, we keep
762     // track of all the focus requesters in this map, with their clientId as the key. This is
763     // used both for focus dispatch and death handling
764     private HashMap<String, FocusRequester> mFocusOwnersForFocusPolicy =
765             new HashMap<String, FocusRequester>();
766 
setFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy)767     void setFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy) {
768         if (policy == null) {
769             return;
770         }
771         synchronized (mAudioFocusLock) {
772             if (isTestFocusPolicy) {
773                 mPreviousFocusPolicy = mFocusPolicy;
774             }
775             mFocusPolicy = policy;
776         }
777     }
778 
unsetFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy)779     void unsetFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy) {
780         if (policy == null) {
781             return;
782         }
783         synchronized (mAudioFocusLock) {
784             if (mFocusPolicy == policy) {
785                 if (isTestFocusPolicy) {
786                     // restore the focus policy that was there before the focus policy test started
787                     mFocusPolicy = mPreviousFocusPolicy;
788                 } else {
789                     mFocusPolicy = null;
790                 }
791             }
792         }
793     }
794 
795     /**
796      * @param pcb non null
797      */
notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb)798     void notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb) {
799         final IAudioPolicyCallback pcb2 = pcb;
800         final Thread thread = new Thread() {
801             @Override
802             public void run() {
803                 synchronized(mAudioFocusLock) {
804                     if (mFocusStack.isEmpty()) {
805                         return;
806                     }
807                     try {
808                         pcb2.notifyAudioFocusGrant(mFocusStack.peek().toAudioFocusInfo(),
809                                 // top of focus stack always has focus
810                                 AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
811                     } catch (RemoteException e) {
812                         Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
813                                 + pcb2.asBinder(), e);
814                     }
815                 }
816             }
817         };
818         thread.start();
819     }
820 
821     /**
822      * Called synchronized on mAudioFocusLock
823      */
notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult)824     void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) {
825         for (IAudioPolicyCallback pcb : mFocusFollowers) {
826             try {
827                 // oneway
828                 pcb.notifyAudioFocusGrant(afi, requestResult);
829             } catch (RemoteException e) {
830                 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
831                         + pcb.asBinder(), e);
832             }
833         }
834     }
835 
836     /**
837      * Called synchronized on mAudioFocusLock
838      */
notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched)839     void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) {
840         for (IAudioPolicyCallback pcb : mFocusFollowers) {
841             try {
842                 // oneway
843                 pcb.notifyAudioFocusLoss(afi, wasDispatched);
844             } catch (RemoteException e) {
845                 Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback "
846                         + pcb.asBinder(), e);
847             }
848         }
849     }
850 
851     /**
852      * Called synchronized on mAudioFocusLock.
853      * Can only be called with an external focus policy installed (mFocusPolicy != null)
854      * @param afi
855      * @param fd
856      * @param cb binder of the focus requester
857      * @return true if the external audio focus policy (if any) can handle the focus request,
858      *     and false if there was any error handling the request (e.g. error talking to policy,
859      *     focus requester is already dead)
860      */
notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, IAudioFocusDispatcher fd, @NonNull IBinder cb)861     boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi,
862             IAudioFocusDispatcher fd, @NonNull IBinder cb) {
863         if (DEBUG) {
864             Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId()
865             + " dispatcher=" + fd);
866         }
867         synchronized (mExtFocusChangeLock) {
868             afi.setGen(mExtFocusChangeCounter++);
869         }
870         final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
871         boolean keepTrack = false;
872         if (existingFr != null) {
873             if (!existingFr.hasSameDispatcher(fd)) {
874                 existingFr.release();
875                 keepTrack = true;
876             }
877         } else {
878             keepTrack = true;
879         }
880         if (keepTrack) {
881             final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb);
882             try {
883                 cb.linkToDeath(hdlr, 0);
884             } catch (RemoteException e) {
885                 // client has already died!
886                 return false;
887             }
888             // new focus (future) focus owner to keep track of
889             mFocusOwnersForFocusPolicy.put(afi.getClientId(),
890                     new FocusRequester(afi, fd, cb, hdlr, this, mEventLogger));
891         }
892 
893         try {
894             //oneway
895             mFocusPolicy.notifyAudioFocusRequest(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
896             return true;
897         } catch (RemoteException e) {
898             Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback "
899                     + mFocusPolicy.asBinder(), e);
900         }
901         return false;
902     }
903 
setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult)904     void setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult) {
905         synchronized (mExtFocusChangeLock) {
906             if (afi.getGen() > mExtFocusChangeCounter) {
907                 return;
908             }
909         }
910         synchronized (mAudioFocusLock) {
911             FocusRequester fr = getFocusRequesterLocked(afi.getClientId(),
912                     /* shouldRemove= */ requestResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED);
913             if (fr != null) {
914                 fr.dispatchFocusResultFromExtPolicy(requestResult);
915                 // if fade is enabled for external focus policies, apply it when setting
916                 // focus result as well
917                 if (enableFadeManagerConfiguration()) {
918                     fr.handleFocusGainFromRequest(requestResult);
919                 }
920             }
921         }
922     }
923 
924     /**
925      * Called synchronized on mAudioFocusLock
926      * @param afi
927      * @return true if the external audio focus policy (if any) is handling the focus request
928      */
notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi)929     boolean notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi) {
930         if (mFocusPolicy == null) {
931             return false;
932         }
933         final FocusRequester fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
934         if (fr != null) {
935             fr.release();
936         }
937         try {
938             //oneway
939             mFocusPolicy.notifyAudioFocusAbandon(afi);
940         } catch (RemoteException e) {
941             Log.e(TAG, "Can't call notifyAudioFocusAbandon() on IAudioPolicyCallback "
942                     + mFocusPolicy.asBinder(), e);
943         }
944         return true;
945     }
946 
947     /** see AudioManager.dispatchFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) */
dispatchFocusChange(AudioFocusInfo afi, int focusChange)948     int dispatchFocusChange(AudioFocusInfo afi, int focusChange) {
949         if (DEBUG) {
950             Log.v(TAG, "dispatchFocusChange " + focusChange + " to afi client="
951                     + afi.getClientId());
952         }
953         synchronized (mAudioFocusLock) {
954             FocusRequester fr = getFocusRequesterLocked(afi.getClientId(),
955                     /* shouldRemove= */ focusChange == AudioManager.AUDIOFOCUS_LOSS);
956             if (fr == null) {
957                 if (DEBUG) {
958                     Log.v(TAG, "> failed: no such focus requester known");
959                 }
960                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
961             }
962             return fr.dispatchFocusChange(focusChange, "audiomanager");
963         }
964     }
965 
dispatchFocusChangeWithFade(AudioFocusInfo afi, int focusChange, List<AudioFocusInfo> otherActiveAfis)966     int dispatchFocusChangeWithFade(AudioFocusInfo afi, int focusChange,
967             List<AudioFocusInfo> otherActiveAfis) {
968         if (DEBUG) {
969             Log.v(TAG, "dispatchFocusChangeWithFade " + AudioManager.audioFocusToString(focusChange)
970                     + " to afi client=" + afi.getClientId()
971                     + " other active afis=" + otherActiveAfis);
972         }
973 
974         synchronized (mAudioFocusLock) {
975             String clientId = afi.getClientId();
976             // do not remove the entry since it can be posted for fade
977             FocusRequester fr = getFocusRequesterLocked(clientId, /* shouldRemove= */ false);
978             if (fr == null) {
979                 if (DEBUG) {
980                     Log.v(TAG, "> failed: no such focus requester known");
981                 }
982                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
983             }
984 
985             // convert other AudioFocusInfo to corresponding FocusRequester
986             ArrayList<FocusRequester> otherActiveFrs = new ArrayList<>();
987             for (int index = 0; index < otherActiveAfis.size(); index++) {
988                 FocusRequester otherFr = getFocusRequesterLocked(
989                         otherActiveAfis.get(index).getClientId(), /* shouldRemove= */ false);
990                 if (otherFr == null) {
991                     continue;
992                 }
993                 otherActiveFrs.add(otherFr);
994             }
995 
996             // TODO log
997             int status = fr.dispatchFocusChangeWithFadeLocked(focusChange, otherActiveFrs);
998             if (status != AudioManager.AUDIOFOCUS_REQUEST_DELAYED
999                     && focusChange == AudioManager.AUDIOFOCUS_LOSS) {
1000                 mFocusOwnersForFocusPolicy.remove(clientId);
1001             }
1002 
1003             return status;
1004         }
1005     }
1006 
1007     @GuardedBy("mAudioFocusLock")
getFocusRequesterLocked(String clientId, boolean shouldRemove)1008     private FocusRequester getFocusRequesterLocked(String clientId, boolean shouldRemove) {
1009         if (mFocusPolicy == null) {
1010             if (DEBUG) {
1011                 Log.v(TAG, "> failed: no focus policy");
1012             }
1013             return null;
1014         }
1015 
1016         FocusRequester fr;
1017         if (shouldRemove) {
1018             fr = mFocusOwnersForFocusPolicy.remove(clientId);
1019         } else {
1020             fr = mFocusOwnersForFocusPolicy.get(clientId);
1021         }
1022 
1023         if (fr == null && DEBUG) {
1024             Log.v(TAG, "> failed: no such focus requester known");
1025         }
1026         return fr;
1027     }
1028 
dumpExtFocusPolicyFocusOwners(PrintWriter pw)1029     private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) {
1030         final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
1031         final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
1032         while (ownerIterator.hasNext()) {
1033             final Entry<String, FocusRequester> owner = ownerIterator.next();
1034             final FocusRequester fr = owner.getValue();
1035             fr.dump(pw);
1036         }
1037     }
1038 
getCurrentAudioFocus()1039     protected int getCurrentAudioFocus() {
1040         synchronized(mAudioFocusLock) {
1041             if (mFocusStack.empty()) {
1042                 return AudioManager.AUDIOFOCUS_NONE;
1043             } else {
1044                 return mFocusStack.peek().getGainRequest();
1045             }
1046         }
1047     }
1048 
1049     /**
1050      * Delay after entering ringing or call mode after which the framework will mute streams
1051      * that are still playing.
1052      */
1053     private static final int RING_CALL_MUTING_ENFORCEMENT_DELAY_MS = 100;
1054 
1055     /**
1056      * Usages to mute when the device rings or is in a call
1057      */
1058     private final static int[] USAGES_TO_MUTE_IN_RING_OR_CALL =
1059         { AudioAttributes.USAGE_MEDIA, AudioAttributes.USAGE_GAME };
1060 
1061     /**
1062      * Return the volume ramp time expected before playback with the given AudioAttributes would
1063      * start after gaining audio focus.
1064      * @param attr attributes of the sound about to start playing
1065      * @return time in ms
1066      */
getFocusRampTimeMs(int focusGain, AudioAttributes attr)1067     protected static int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
1068         switch (attr.getUsage()) {
1069             case AudioAttributes.USAGE_MEDIA:
1070             case AudioAttributes.USAGE_GAME:
1071             case AudioAttributes.USAGE_SPEAKER_CLEANUP:
1072                 return 1000;
1073             case AudioAttributes.USAGE_ALARM:
1074             case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
1075             case AudioAttributes.USAGE_ASSISTANT:
1076             case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY:
1077             case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
1078             case AudioAttributes.USAGE_ANNOUNCEMENT:
1079                 return 700;
1080             case AudioAttributes.USAGE_VOICE_COMMUNICATION:
1081             case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING:
1082             case AudioAttributes.USAGE_NOTIFICATION:
1083             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
1084             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
1085             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
1086             case AudioAttributes.USAGE_NOTIFICATION_EVENT:
1087             case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION:
1088             case AudioAttributes.USAGE_VEHICLE_STATUS:
1089                 return 500;
1090             case AudioAttributes.USAGE_EMERGENCY:
1091             case AudioAttributes.USAGE_SAFETY:
1092             case AudioAttributes.USAGE_UNKNOWN:
1093             default:
1094                 return 0;
1095         }
1096     }
1097 
1098     /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int)
1099      * @param aa
1100      * @param focusChangeHint
1101      * @param cb
1102      * @param fd
1103      * @param clientId
1104      * @param callingPackageName
1105      * @param flags
1106      * @param sdk
1107      * @param forceDuck only true if
1108      *     {@link android.media.AudioFocusRequest.Builder#setFocusGain(int)} was set to true for
1109      *                  accessibility.
1110      * @param testUid ignored if flags doesn't contain AudioManager.AUDIOFOCUS_FLAG_TEST
1111      *                otherwise the UID being injected for testing
1112      * @param permissionOverridesCheck true if permission checks guaranteed that the call should
1113      *                                 go through, false otherwise (e.g. non-privileged caller)
1114      * @return
1115      */
requestAudioFocus(@onNull AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, int flags, int sdk, boolean forceDuck, int testUid, boolean permissionOverridesCheck)1116     protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
1117             IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
1118             int flags, int sdk, boolean forceDuck, int testUid,
1119             boolean permissionOverridesCheck) {
1120         new MediaMetrics.Item(mMetricsId)
1121                 .setUid(Binder.getCallingUid())
1122                 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
1123                 .set(MediaMetrics.Property.CLIENT_NAME, clientId)
1124                 .set(MediaMetrics.Property.EVENT, "requestAudioFocus")
1125                 .set(MediaMetrics.Property.FLAGS, flags)
1126                 .set(MediaMetrics.Property.FOCUS_CHANGE_HINT,
1127                         AudioManager.audioFocusToString(focusChangeHint))
1128                 //.set(MediaMetrics.Property.SDK, sdk)
1129                 .record();
1130 
1131         // when using the test API, a fake UID can be injected (testUid is ignored otherwise)
1132         // note that the test on flags is not a mask test on purpose, AUDIOFOCUS_FLAG_TEST is
1133         // supposed to be alone in bitfield
1134         final int uid = (flags == AudioManager.AUDIOFOCUS_FLAG_TEST)
1135                 ? testUid : Binder.getCallingUid();
1136         mEventLogger.enqueue((new EventLogger.StringEvent(
1137                 "requestAudioFocus() from uid/pid " + uid
1138                     + "/" + Binder.getCallingPid()
1139                     + " AA=" + aa.usageToString() + "/" + aa.contentTypeToString()
1140                     + " clientId=" + clientId + " callingPack=" + callingPackageName
1141                     + " req=" + focusChangeHint
1142                     + " flags=0x" + Integer.toHexString(flags)
1143                     + " sdk=" + sdk))
1144                 .printLog(TAG));
1145         // we need a valid binder callback for clients
1146         if (!cb.pingBinder()) {
1147             Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
1148             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1149         }
1150 
1151         synchronized(mAudioFocusLock) {
1152             // check whether a focus freeze is in place and filter
1153             if (isFocusFrozenForTest()) {
1154                 int focusRequesterUid;
1155                 if ((flags & AudioManager.AUDIOFOCUS_FLAG_TEST)
1156                         == AudioManager.AUDIOFOCUS_FLAG_TEST) {
1157                     focusRequesterUid = testUid;
1158                 } else {
1159                     focusRequesterUid = Binder.getCallingUid();
1160                 }
1161                 if (isFocusFrozenForTestForUid(focusRequesterUid)) {
1162                     Log.i(TAG, "requestAudioFocus: focus frozen for test for uid:"
1163                             + focusRequesterUid);
1164                     return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1165                 }
1166                 Log.i(TAG, "requestAudioFocus: focus frozen for test but uid:" + focusRequesterUid
1167                         + " is exempt");
1168             }
1169 
1170             if (mFocusStack.size() > MAX_STACK_SIZE) {
1171                 Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()");
1172                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1173             }
1174 
1175             boolean enteringRingOrCall = !mRingOrCallActive
1176                     & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
1177             if (enteringRingOrCall) { mRingOrCallActive = true; }
1178 
1179             final AudioFocusInfo afiForExtPolicy;
1180             if (mFocusPolicy != null) {
1181                 // construct AudioFocusInfo as it will be communicated to audio focus policy
1182                 afiForExtPolicy = new AudioFocusInfo(aa, uid,
1183                         clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/,
1184                         flags, sdk);
1185             } else {
1186                 afiForExtPolicy = null;
1187             }
1188 
1189             // handle delayed focus
1190             boolean focusGrantDelayed = false;
1191             if (!canReassignAudioFocus()) {
1192                 if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
1193                     return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1194                 } else {
1195                     // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be
1196                     // granted right now, so the requester will be inserted in the focus stack
1197                     // to receive focus later
1198                     focusGrantDelayed = true;
1199                 }
1200             }
1201 
1202             // external focus policy?
1203             if (mFocusPolicy != null) {
1204                 if (notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, fd, cb)) {
1205                     // stop handling focus request here as it is handled by external audio
1206                     // focus policy (return code will be handled in AudioManager)
1207                     return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY;
1208                 } else {
1209                     // an error occured, client already dead, bail early
1210                     return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1211                 }
1212             }
1213 
1214             // handle the potential premature death of the new holder of the focus
1215             // (premature death == death before abandoning focus)
1216             // Register for client death notification
1217             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
1218 
1219             try {
1220                 cb.linkToDeath(afdh, 0);
1221             } catch (RemoteException e) {
1222                 // client has already died!
1223                 Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
1224                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1225             }
1226 
1227             if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
1228                 // if focus is already owned by this client and the reason for acquiring the focus
1229                 // hasn't changed, don't do anything
1230                 final FocusRequester fr = mFocusStack.peek();
1231                 if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {
1232                     // unlink death handler so it can be gc'ed.
1233                     // linkToDeath() creates a JNI global reference preventing collection.
1234                     cb.unlinkToDeath(afdh, 0);
1235                     notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(),
1236                             AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1237                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1238                 }
1239                 // the reason for the audio focus request has changed: remove the current top of
1240                 // stack and respond as if we had a new focus owner
1241                 if (!focusGrantDelayed) {
1242                     mFocusStack.pop();
1243                     // the entry that was "popped" is the same that was "peeked" above
1244                     fr.release();
1245                 }
1246             }
1247 
1248             // focus requester might already be somewhere below in the stack, remove it
1249             removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
1250 
1251             final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
1252                     clientId, afdh, callingPackageName, uid, this, sdk, mEventLogger);
1253 
1254             if (mMultiAudioFocusEnabled
1255                     && (focusChangeHint == AudioManager.AUDIOFOCUS_GAIN)) {
1256                 if (enteringRingOrCall) {
1257                     if (!mMultiAudioFocusList.isEmpty()) {
1258                         for (FocusRequester multifr : mMultiAudioFocusList) {
1259                             multifr.handleFocusLossFromGain(focusChangeHint, nfr, forceDuck);
1260                         }
1261                     }
1262                 } else {
1263                     boolean needAdd = true;
1264                     if (!mMultiAudioFocusList.isEmpty()) {
1265                         for (FocusRequester multifr : mMultiAudioFocusList) {
1266                             if (multifr.getClientUid() == Binder.getCallingUid()) {
1267                                 needAdd = false;
1268                                 break;
1269                             }
1270                         }
1271                     }
1272                     if (needAdd) {
1273                         mMultiAudioFocusList.add(nfr);
1274                     }
1275                     nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1276                     notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
1277                             AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1278                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1279                 }
1280             }
1281 
1282             if (focusGrantDelayed) {
1283                 // focusGrantDelayed being true implies we can't reassign focus right now
1284                 // which implies the focus stack is not empty.
1285                 final int requestResult = pushBelowLockedFocusOwnersAndPropagate(nfr);
1286                 if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
1287                     notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);
1288                 }
1289                 return requestResult;
1290             } else {
1291                 // propagate the focus change through the stack
1292                 propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck);
1293 
1294                 // push focus requester at the top of the audio focus stack
1295                 mFocusStack.push(nfr);
1296                 nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1297             }
1298             notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
1299                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1300 
1301             if (ENFORCE_MUTING_FOR_RING_OR_CALL & enteringRingOrCall) {
1302                 runAudioCheckerForRingOrCallAsync(true/*enteringRingOrCall*/);
1303             }
1304         }//synchronized(mAudioFocusLock)
1305 
1306         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1307     }
1308 
1309     /**
1310      * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
1311      * */
abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, String callingPackageName)1312     protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa,
1313             String callingPackageName) {
1314         new MediaMetrics.Item(mMetricsId)
1315                 .setUid(Binder.getCallingUid())
1316                 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
1317                 .set(MediaMetrics.Property.CLIENT_NAME, clientId)
1318                 .set(MediaMetrics.Property.EVENT, "abandonAudioFocus")
1319                 .record();
1320 
1321         // AudioAttributes are currently ignored, to be used for zones / a11y
1322         mEventLogger.enqueue((new EventLogger.StringEvent(
1323                 "abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
1324                     + "/" + Binder.getCallingPid()
1325                     + " clientId=" + clientId + " callingPack=" + callingPackageName))
1326                 .printLog(TAG));
1327         try {
1328             // this will take care of notifying the new focus owner if needed
1329             synchronized(mAudioFocusLock) {
1330                 // external focus policy?
1331                 if (mFocusPolicy != null) {
1332                     final AudioFocusInfo afi = new AudioFocusInfo(aa, Binder.getCallingUid(),
1333                             clientId, callingPackageName, 0 /*gainRequest*/, 0 /*lossReceived*/,
1334                             0 /*flags*/, 0 /* sdk n/a here*/);
1335                     if (notifyExtFocusPolicyFocusAbandon_syncAf(afi)) {
1336                         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1337                     }
1338                 }
1339 
1340                 boolean exitingRingOrCall = mRingOrCallActive
1341                         & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
1342                 if (exitingRingOrCall) { mRingOrCallActive = false; }
1343 
1344                 removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/);
1345 
1346                 if (ENFORCE_MUTING_FOR_RING_OR_CALL & exitingRingOrCall) {
1347                     runAudioCheckerForRingOrCallAsync(false/*enteringRingOrCall*/);
1348                 }
1349             }
1350         } catch (java.util.ConcurrentModificationException cme) {
1351             // Catching this exception here is temporary. It is here just to prevent
1352             // a crash seen when the "Silent" notification is played. This is believed to be fixed
1353             // but this try catch block is left just to be safe.
1354             Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
1355             cme.printStackTrace();
1356         }
1357 
1358         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1359     }
1360 
1361     /**
1362      * Reference to the caller of {@link #enterAudioFocusFreezeForTest(IBinder, int[])}
1363      * Will be null when there is no focus freeze for test
1364      */
1365     @GuardedBy("mAudioFocusLock")
1366     @Nullable
1367     private IBinder mFocusFreezerForTest = null;
1368 
1369     /**
1370      * The death handler for {@link #mFocusFreezerForTest}
1371      * Will be null when there is no focus freeze for test
1372      */
1373     @GuardedBy("mAudioFocusLock")
1374     @Nullable
1375     private IBinder.DeathRecipient mFocusFreezerDeathHandler = null;
1376 
1377     /**
1378      *  Array of UIDs exempt from focus freeze when focus is frozen for test, null during normal
1379      *  operations.
1380      *  Will be null when there is no focus freeze for test
1381      */
1382     @GuardedBy("mAudioFocusLock")
1383     @Nullable
1384     private int[] mFocusFreezeExemptUids = null;
1385 
1386     @GuardedBy("mAudioFocusLock")
isFocusFrozenForTest()1387     private boolean isFocusFrozenForTest() {
1388         return (mFocusFreezerForTest != null);
1389     }
1390 
1391     /**
1392      * Checks if the given UID can request focus when a focus freeze is in place for a test.
1393      * Focus can be requested if focus is not frozen or if it's frozen but the UID is exempt.
1394      * @param uidToCheck
1395      * @return true if that UID is barred from requesting focus, false if its focus request
1396      *     can proceed being processed
1397      */
1398     @GuardedBy("mAudioFocusLock")
isFocusFrozenForTestForUid(int uidToCheck)1399     private boolean isFocusFrozenForTestForUid(int uidToCheck) {
1400         if (isFocusFrozenForTest()) {
1401             return false;
1402         }
1403         // check the list of exempts (array is not null because we're in a freeze for test
1404         for (int uid : mFocusFreezeExemptUids) {
1405             if (uid == uidToCheck) {
1406                 return false;
1407             }
1408         }
1409         // uid was not found in the exempt list, its focus request is denied
1410         return true;
1411     }
1412 
enterAudioFocusFreezeForTest( @onNull IBinder cb, @NonNull int[] exemptedUids)1413     protected boolean enterAudioFocusFreezeForTest(
1414             @NonNull IBinder cb, @NonNull int[] exemptedUids) {
1415         Log.i(TAG, "enterAudioFocusFreezeForTest UIDs exempt:" + Arrays.toString(exemptedUids));
1416         synchronized (mAudioFocusLock) {
1417             if (mFocusFreezerForTest != null) {
1418                 Log.e(TAG, "Error enterAudioFocusFreezeForTest: focus already frozen");
1419                 return false;
1420             }
1421             // new focus freeze, register death handler
1422             try {
1423                 mFocusFreezerDeathHandler = new IBinder.DeathRecipient() {
1424                     @Override
1425                     public void binderDied() {
1426                         Log.i(TAG, "Audio focus freezer died, exiting focus freeze for test");
1427                         releaseFocusFreeze();
1428                     }
1429                 };
1430                 cb.linkToDeath(mFocusFreezerDeathHandler, 0);
1431                 mFocusFreezerForTest = cb;
1432                 mFocusFreezeExemptUids = exemptedUids.clone();
1433             } catch (RemoteException e) {
1434                 // client has already died!
1435                 mFocusFreezerForTest = null;
1436                 mFocusFreezeExemptUids = null;
1437                 return false;
1438             }
1439         }
1440         return true;
1441     }
1442 
exitAudioFocusFreezeForTest(@onNull IBinder cb)1443     protected boolean exitAudioFocusFreezeForTest(@NonNull IBinder cb) {
1444         synchronized (mAudioFocusLock) {
1445             if (mFocusFreezerForTest != cb) {
1446                 Log.e(TAG, "Error exitAudioFocusFreezeForTest: "
1447                         + ((mFocusFreezerForTest == null)
1448                         ? "call to exit while not frozen"
1449                         : "call to exit not coming from freeze owner"));
1450                 return false;
1451             }
1452             mFocusFreezerForTest.unlinkToDeath(mFocusFreezerDeathHandler, 0);
1453             releaseFocusFreeze();
1454         }
1455         return true;
1456     }
1457 
releaseFocusFreeze()1458     private void releaseFocusFreeze() {
1459         synchronized (mAudioFocusLock) {
1460             mFocusFreezerDeathHandler = null;
1461             mFocusFreezeExemptUids = null;
1462             mFocusFreezerForTest = null;
1463         }
1464     }
1465 
unregisterAudioFocusClient(String clientId)1466     protected void unregisterAudioFocusClient(String clientId) {
1467         synchronized(mAudioFocusLock) {
1468             removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/);
1469         }
1470     }
1471 
runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall)1472     private void runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall) {
1473         new Thread() {
1474             public void run() {
1475                 if (enteringRingOrCall) {
1476                     try {
1477                         Thread.sleep(RING_CALL_MUTING_ENFORCEMENT_DELAY_MS);
1478                     } catch (InterruptedException e) {
1479                         e.printStackTrace();
1480                     }
1481                 }
1482                 synchronized (mAudioFocusLock) {
1483                     // since the new thread starting running the state could have changed, so
1484                     // we need to check again mRingOrCallActive, not enteringRingOrCall
1485                     if (mRingOrCallActive) {
1486                         mFocusEnforcer.mutePlayersForCall(USAGES_TO_MUTE_IN_RING_OR_CALL);
1487                     } else {
1488                         mFocusEnforcer.unmutePlayersForCall();
1489                     }
1490                 }
1491             }
1492         }.start();
1493     }
1494 
updateMultiAudioFocus(boolean enabled)1495     public void updateMultiAudioFocus(boolean enabled) {
1496         Log.d(TAG, "updateMultiAudioFocus( " + enabled + " )");
1497         mMultiAudioFocusEnabled = enabled;
1498         final ContentResolver cr = mContext.getContentResolver();
1499         Settings.System.putIntForUser(cr,
1500                 Settings.System.MULTI_AUDIO_FOCUS_ENABLED, enabled ? 1 : 0, cr.getUserId());
1501         if (!mFocusStack.isEmpty()) {
1502             final FocusRequester fr = mFocusStack.peek();
1503             fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false);
1504         }
1505         if (!enabled) {
1506             if (!mMultiAudioFocusList.isEmpty()) {
1507                 for (FocusRequester multifr : mMultiAudioFocusList) {
1508                     multifr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false);
1509                 }
1510                 mMultiAudioFocusList.clear();
1511             }
1512         }
1513     }
1514 
getMultiAudioFocusEnabled()1515     public boolean getMultiAudioFocusEnabled() {
1516         return mMultiAudioFocusEnabled;
1517     }
1518 
getFadeOutDurationOnFocusLossMillis(AudioAttributes aa)1519     /*package*/ long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) {
1520         if (!ENFORCE_FADEOUT_FOR_FOCUS_LOSS) {
1521             return 0;
1522         }
1523         return getFadeOutDurationMillis(aa);
1524     }
1525 
dumpMultiAudioFocus(PrintWriter pw)1526     private void dumpMultiAudioFocus(PrintWriter pw) {
1527         pw.println("Multi Audio Focus enabled :" + mMultiAudioFocusEnabled);
1528         if (!mMultiAudioFocusList.isEmpty()) {
1529             pw.println("Multi Audio Focus List:");
1530             pw.println("------------------------------");
1531             for (FocusRequester multifr : mMultiAudioFocusList) {
1532                 multifr.dump(pw);
1533             }
1534             pw.println("------------------------------");
1535         }
1536     }
1537 
1538     //=================================================================
1539     // Async focus events
postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs)1540     void postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs) {
1541         if (DEBUG) {
1542             Log.v(TAG, "postDelayedLossAfterFade loser=" + focusLoser.getPackageName());
1543         }
1544         mFocusHandler.sendMessageDelayed(
1545                 mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser), delayMs);
1546     }
1547 
postForgetUidLater(FocusRequester focusRequester)1548     private void postForgetUidLater(FocusRequester focusRequester) {
1549         mFocusHandler.sendMessageDelayed(
1550                 mFocusHandler.obtainMessage(MSL_L_FORGET_UID,
1551                         new ForgetFadeUidInfo(focusRequester.getClientUid())),
1552                 getFadeInDelayForOffendersMillis(focusRequester.getAudioAttributes()));
1553     }
1554 
1555     //=================================================================
1556     // Message handling
1557     private Handler mFocusHandler;
1558     private HandlerThread mFocusThread;
1559 
1560     /**
1561      * dispatch a focus loss after an app has been faded out. Focus loser is to be released
1562      * after dispatch as it has already left the stack
1563      * args:
1564      *     msg.obj: the audio focus loser
1565      *         type:FocusRequester
1566      */
1567     private static final int MSG_L_FOCUS_LOSS_AFTER_FADE = 1;
1568 
1569     private static final int MSL_L_FORGET_UID = 2;
1570 
initFocusThreading()1571     private void initFocusThreading() {
1572         mFocusThread = new HandlerThread(TAG);
1573         mFocusThread.start();
1574         mFocusHandler = new Handler(mFocusThread.getLooper()) {
1575             @Override
1576             public void handleMessage(Message msg) {
1577                 switch (msg.what) {
1578                     case MSG_L_FOCUS_LOSS_AFTER_FADE:
1579                         if (DEBUG) {
1580                             Log.d(TAG, "MSG_L_FOCUS_LOSS_AFTER_FADE loser="
1581                                     + ((FocusRequester) msg.obj).getPackageName());
1582                         }
1583                         synchronized (mAudioFocusLock) {
1584                             final FocusRequester loser = (FocusRequester) msg.obj;
1585                             if (loser.isInFocusLossLimbo()) {
1586                                 loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS, "loss after fade");
1587                                 loser.release();
1588                                 postForgetUidLater(loser);
1589                             }
1590                         }
1591                         break;
1592 
1593                     case MSL_L_FORGET_UID:
1594                         final int uid = ((ForgetFadeUidInfo) msg.obj).mUid;
1595                         if (DEBUG) {
1596                             Log.d(TAG, "MSL_L_FORGET_UID uid=" + uid);
1597                         }
1598                         mFocusEnforcer.forgetUid(uid);
1599                         break;
1600                     default:
1601                         break;
1602                 }
1603             }
1604         };
1605     }
1606 
1607     /**
1608      * Class to associate a UID with a scheduled event to "forget" a UID for the fade out behavior.
1609      * Having a class with an equals() override allows using Handler.removeEqualsMessage() to
1610      * unschedule events when needed. Here we need to unschedule the "unfading out" == "forget uid"
1611      * whenever a new, more recent, focus related event happens before this one is handled.
1612      */
1613     private static final class ForgetFadeUidInfo {
1614         private final int mUid;
1615 
ForgetFadeUidInfo(int uid)1616         ForgetFadeUidInfo(int uid) {
1617             mUid = uid;
1618         }
1619 
1620         @Override
equals(Object o)1621         public boolean equals(Object o) {
1622             if (this == o) {
1623                 return true;
1624             }
1625             if (o == null || getClass() != o.getClass()) {
1626                 return false;
1627             }
1628             final ForgetFadeUidInfo f = (ForgetFadeUidInfo) o;
1629             if (f.mUid != mUid) {
1630                 return false;
1631             }
1632             return true;
1633         }
1634 
1635         @Override
hashCode()1636         public int hashCode() {
1637             return mUid;
1638         }
1639     }
1640 }
1641