1 /*
2  * Copyright (C) 2016 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.AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_MUTE;
20 import static android.media.AudioPlaybackConfiguration.MUTED_BY_APP_OPS;
21 import static android.media.AudioPlaybackConfiguration.MUTED_BY_CLIENT_VOLUME;
22 import static android.media.AudioPlaybackConfiguration.MUTED_BY_MASTER;
23 import static android.media.AudioPlaybackConfiguration.MUTED_BY_STREAM_MUTED;
24 import static android.media.AudioPlaybackConfiguration.MUTED_BY_STREAM_VOLUME;
25 import static android.media.AudioPlaybackConfiguration.MUTED_BY_VOLUME_SHAPER;
26 import static android.media.AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
27 import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED;
28 
29 import static com.android.media.audio.Flags.portToPiidSimplification;
30 
31 import android.annotation.NonNull;
32 import android.annotation.Nullable;
33 import android.content.Context;
34 import android.content.pm.PackageManager;
35 import android.media.AudioAttributes;
36 import android.media.AudioDeviceAttributes;
37 import android.media.AudioDeviceInfo;
38 import android.media.AudioManager;
39 import android.media.AudioPlaybackConfiguration;
40 import android.media.AudioPlaybackConfiguration.FormatInfo;
41 import android.media.AudioPlaybackConfiguration.PlayerMuteEvent;
42 import android.media.AudioSystem;
43 import android.media.FadeManagerConfiguration;
44 import android.media.IPlaybackConfigDispatcher;
45 import android.media.PlayerBase;
46 import android.media.VolumeShaper;
47 import android.os.Binder;
48 import android.os.Handler;
49 import android.os.HandlerThread;
50 import android.os.IBinder;
51 import android.os.Message;
52 import android.os.PersistableBundle;
53 import android.os.RemoteException;
54 import android.os.UserHandle;
55 import android.text.TextUtils;
56 import android.util.Log;
57 import android.util.SparseIntArray;
58 
59 import com.android.internal.annotations.GuardedBy;
60 import com.android.internal.util.ArrayUtils;
61 import com.android.server.utils.EventLogger;
62 
63 import java.io.PrintWriter;
64 import java.text.DateFormat;
65 import java.util.ArrayList;
66 import java.util.Arrays;
67 import java.util.Collections;
68 import java.util.Date;
69 import java.util.HashMap;
70 import java.util.Iterator;
71 import java.util.List;
72 import java.util.Set;
73 import java.util.concurrent.ConcurrentLinkedQueue;
74 import java.util.function.Consumer;
75 
76 /**
77  * Class to receive and dispatch updates from AudioSystem about recording configurations.
78  */
79 public final class PlaybackActivityMonitor
80         implements AudioPlaybackConfiguration.PlayerDeathMonitor, PlayerFocusEnforcer {
81 
82     public static final String TAG = "AS.PlaybackActivityMon";
83 
84     /*package*/ static final boolean DEBUG = false;
85     /*package*/ static final int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;
86     /*package*/ static final int VOLUME_SHAPER_SYSTEM_FADEOUT_ID = 2;
87     /*package*/ static final int VOLUME_SHAPER_SYSTEM_MUTE_AWAIT_CONNECTION_ID = 3;
88     /*package*/ static final int VOLUME_SHAPER_SYSTEM_STRONG_DUCK_ID = 4;
89     /*package*/ static final String EVENT_TYPE_FADE_OUT = "fading out";
90     /*package*/ static final String EVENT_TYPE_FADE_IN = "fading in";
91 
92     // ducking settings for a "normal duck" at -14dB
93     private static final VolumeShaper.Configuration DUCK_VSHAPE =
94             new VolumeShaper.Configuration.Builder()
95                 .setId(VOLUME_SHAPER_SYSTEM_DUCK_ID)
96                 .setCurve(new float[] { 0.f, 1.f } /* times */,
97                     new float[] { 1.f, 0.2f } /* volumes */)
98                 .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
99                 .setDuration(MediaFocusControl.getFocusRampTimeMs(
100                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
101                     new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION)
102                             .build()))
103                 .build();
104     private static final VolumeShaper.Configuration DUCK_ID =
105             new VolumeShaper.Configuration(VOLUME_SHAPER_SYSTEM_DUCK_ID);
106 
107     // ducking settings for a "strong duck" at -35dB (attenuation factor of 0.017783)
108     private static final VolumeShaper.Configuration STRONG_DUCK_VSHAPE =
109             new VolumeShaper.Configuration.Builder()
110                 .setId(VOLUME_SHAPER_SYSTEM_STRONG_DUCK_ID)
111                 .setCurve(new float[] { 0.f, 1.f } /* times */,
112                         new float[] { 1.f, 0.017783f } /* volumes */)
113                 .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
114                 .setDuration(MediaFocusControl.getFocusRampTimeMs(
115                         AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
116                         new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION)
117                                 .build()))
118                     .build();
119     private static final VolumeShaper.Configuration STRONG_DUCK_ID =
120             new VolumeShaper.Configuration(VOLUME_SHAPER_SYSTEM_STRONG_DUCK_ID);
121 
122     private static final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED =
123             new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY)
124                     .createIfNeeded()
125                     .build();
126 
127     private static final long UNMUTE_DURATION_MS = 100;
128     private static final VolumeShaper.Configuration MUTE_AWAIT_CONNECTION_VSHAPE =
129             new VolumeShaper.Configuration.Builder()
130                     .setId(VOLUME_SHAPER_SYSTEM_MUTE_AWAIT_CONNECTION_ID)
131                     .setCurve(new float[] { 0.f, 1.f } /* times */,
132                             new float[] { 1.f, 0.f } /* volumes */)
133                     .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
134                     // even though we specify a duration, it's only used for the unmute,
135                     // for muting this volume shaper is run with PLAY_SKIP_RAMP
136                     .setDuration(UNMUTE_DURATION_MS)
137                     .build();
138 
139     // TODO support VolumeShaper on those players
140     private static final int[] UNDUCKABLE_PLAYER_TYPES = {
141             AudioPlaybackConfiguration.PLAYER_TYPE_AAUDIO,
142             AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL,
143     };
144 
145     // like a PLAY_CREATE_IF_NEEDED operation but with a skip to the end of the ramp
146     private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
147             new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
148 
149     private final ConcurrentLinkedQueue<PlayMonitorClient> mClients = new ConcurrentLinkedQueue<>();
150 
151     private final Object mPlayerLock = new Object();
152     @GuardedBy("mPlayerLock")
153     private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers =
154             new HashMap<Integer, AudioPlaybackConfiguration>();
155 
156     @GuardedBy("mPlayerLock")
157     private final SparseIntArray mPiidToPortId = new SparseIntArray();
158     @GuardedBy("mPlayerLock")
159     private final SparseIntArray mPortIdToPiid = new SparseIntArray();
160 
161     private final Context mContext;
162     private int mSavedAlarmVolume = -1;
163     private final int mMaxAlarmVolume;
164     private int mPrivilegedAlarmActiveCount = 0;
165     private final Consumer<AudioDeviceAttributes> mMuteAwaitConnectionTimeoutCb;
166     private final FadeOutManager mFadeOutManager = new FadeOutManager();
167 
PlaybackActivityMonitor(Context context, int maxAlarmVolume, Consumer<AudioDeviceAttributes> muteTimeoutCallback)168     PlaybackActivityMonitor(Context context, int maxAlarmVolume,
169             Consumer<AudioDeviceAttributes> muteTimeoutCallback) {
170         mContext = context;
171         mMaxAlarmVolume = maxAlarmVolume;
172         PlayMonitorClient.sListenerDeathMonitor = this;
173         AudioPlaybackConfiguration.sPlayerDeathMonitor = this;
174         mMuteAwaitConnectionTimeoutCb = muteTimeoutCallback;
175         initEventHandler();
176     }
177 
178     //=================================================================
179     private final ArrayList<Integer> mBannedUids = new ArrayList<Integer>();
180 
181     // see AudioManagerInternal.disableAudioForUid(boolean disable, int uid)
disableAudioForUid(boolean disable, int uid)182     public void disableAudioForUid(boolean disable, int uid) {
183         synchronized(mPlayerLock) {
184             final int index = mBannedUids.indexOf(new Integer(uid));
185             if (index >= 0) {
186                 if (!disable) {
187                     if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
188                         sEventLogger.enqueue(new EventLogger.StringEvent("unbanning uid:" + uid));
189                     }
190                     mBannedUids.remove(index);
191                     // nothing else to do, future playback requests from this uid are ok
192                 } // no else to handle, uid already present, so disabling again is no-op
193             } else {
194                 if (disable) {
195                     for (AudioPlaybackConfiguration apc : mPlayers.values()) {
196                         checkBanPlayer(apc, uid);
197                     }
198                     if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
199                         sEventLogger.enqueue(new EventLogger.StringEvent("banning uid:" + uid));
200                     }
201                     mBannedUids.add(new Integer(uid));
202                 } // no else to handle, uid already not in list, so enabling again is no-op
203             }
204         }
205     }
206 
checkBanPlayer(@onNull AudioPlaybackConfiguration apc, int uid)207     private boolean checkBanPlayer(@NonNull AudioPlaybackConfiguration apc, int uid) {
208         final boolean toBan = (apc.getClientUid() == uid);
209         if (toBan) {
210             final int piid = apc.getPlayerInterfaceId();
211             try {
212                 Log.v(TAG, "banning player " + piid + " uid:" + uid);
213                 apc.getPlayerProxy().pause();
214             } catch (Exception e) {
215                 Log.e(TAG, "error banning player " + piid + " uid:" + uid, e);
216             }
217         }
218         return toBan;
219     }
220 
221     //=================================================================
222     // Player to ignore (only handling single player, designed for ignoring
223     // in the logs one specific player such as the touch sounds player)
224     @GuardedBy("mPlayerLock")
225     private ArrayList<Integer> mDoNotLogPiidList = new ArrayList<>();
226 
ignorePlayerIId(int doNotLogPiid)227     /*package*/ void ignorePlayerIId(int doNotLogPiid) {
228         synchronized (mPlayerLock) {
229             mDoNotLogPiidList.add(doNotLogPiid);
230         }
231     }
232 
233     //=================================================================
234     // Track players and their states
235     // methods playerAttributes, playerEvent, releasePlayer are all oneway calls
236     //  into AudioService. They trigger synchronous dispatchPlaybackChange() which updates
237     //  all listeners as oneway calls.
238 
trackPlayer(PlayerBase.PlayerIdCard pic)239     public int trackPlayer(PlayerBase.PlayerIdCard pic) {
240         final int newPiid = AudioSystem.newAudioPlayerId();
241         if (DEBUG) { Log.v(TAG, "trackPlayer() new piid=" + newPiid); }
242         if (newPiid == PLAYER_PIID_INVALID) {
243             Log.w(TAG, "invalid piid assigned from AudioSystem");
244             return newPiid;
245         }
246         final AudioPlaybackConfiguration apc =
247                 new AudioPlaybackConfiguration(pic, newPiid,
248                         Binder.getCallingUid(), Binder.getCallingPid());
249         apc.init();
250         synchronized (mAllowedCapturePolicies) {
251             int uid = apc.getClientUid();
252             if (mAllowedCapturePolicies.containsKey(uid)) {
253                 updateAllowedCapturePolicy(apc, mAllowedCapturePolicies.get(uid));
254             }
255         }
256         sEventLogger.enqueue(new NewPlayerEvent(apc));
257         synchronized(mPlayerLock) {
258             mPlayers.put(newPiid, apc);
259             maybeMutePlayerAwaitingConnection(apc);
260         }
261         return newPiid;
262     }
263 
playerAttributes(int piid, @NonNull AudioAttributes attr, int binderUid)264     public void playerAttributes(int piid, @NonNull AudioAttributes attr, int binderUid) {
265         final boolean change;
266         synchronized (mAllowedCapturePolicies) {
267             if (mAllowedCapturePolicies.containsKey(binderUid)
268                     && attr.getAllowedCapturePolicy() < mAllowedCapturePolicies.get(binderUid)) {
269                 attr = new AudioAttributes.Builder(attr)
270                         .setAllowedCapturePolicy(mAllowedCapturePolicies.get(binderUid)).build();
271             }
272         }
273         synchronized(mPlayerLock) {
274             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
275             if (checkConfigurationCaller(piid, apc, binderUid)) {
276                 sEventLogger.enqueue(new AudioAttrEvent(piid, attr));
277                 change = apc.handleAudioAttributesEvent(attr);
278             } else {
279                 Log.e(TAG, "Error updating audio attributes");
280                 change = false;
281             }
282         }
283         if (change) {
284             dispatchPlaybackChange(false);
285         }
286     }
287 
288     /**
289      * Update player session ID
290      * @param piid Player id to update
291      * @param sessionId The new audio session ID
292      * @param binderUid Calling binder uid
293      */
playerSessionId(int piid, int sessionId, int binderUid)294     public void playerSessionId(int piid, int sessionId, int binderUid) {
295         final boolean change;
296         synchronized (mPlayerLock) {
297             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
298             if (checkConfigurationCaller(piid, apc, binderUid)) {
299                 change = apc.handleSessionIdEvent(sessionId);
300             } else {
301                 Log.e(TAG, "Error updating audio session");
302                 change = false;
303             }
304         }
305         if (change) {
306             dispatchPlaybackChange(false);
307         }
308     }
309 
310     private static final int FLAGS_FOR_SILENCE_OVERRIDE =
311             AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
312             AudioAttributes.FLAG_BYPASS_MUTE;
313 
checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event)314     private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) {
315         if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID) {
316             return;
317         }
318         if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED ||
319                 apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
320             if ((apc.getAudioAttributes().getAllFlags() & FLAGS_FOR_SILENCE_OVERRIDE)
321                         == FLAGS_FOR_SILENCE_OVERRIDE  &&
322                     apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_ALARM &&
323                     mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
324                             apc.getClientPid(), apc.getClientUid()) ==
325                             PackageManager.PERMISSION_GRANTED) {
326                 if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED &&
327                         apc.getPlayerState() != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
328                     if (mPrivilegedAlarmActiveCount++ == 0) {
329                         mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex(
330                                 AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER);
331                         AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM,
332                                 mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
333                     }
334                 } else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED &&
335                         apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
336                     if (--mPrivilegedAlarmActiveCount == 0) {
337                         if (AudioSystem.getStreamVolumeIndex(
338                                 AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) ==
339                                 mMaxAlarmVolume) {
340                             AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM,
341                                     mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
342                         }
343                     }
344                 }
345             }
346         }
347     }
348 
349     /**
350      * Update player event
351      * @param piid Player id to update
352      * @param event The new player event
353      * @param eventValue The value associated with this event
354      * @param binderUid Calling binder uid
355      */
playerEvent(int piid, int event, int eventValue, int binderUid)356     public void playerEvent(int piid, int event, int eventValue, int binderUid) {
357         if (DEBUG) {
358             Log.v(TAG, TextUtils.formatSimple("playerEvent(piid=%d, event=%s, eventValue=%d)",
359                     piid, AudioPlaybackConfiguration.playerStateToString(event), eventValue));
360         }
361         boolean change;
362         synchronized(mPlayerLock) {
363             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
364             if (apc == null) {
365                 return;
366             }
367 
368             final boolean doNotLog = mDoNotLogPiidList.contains(piid);
369             if (doNotLog && event != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) {
370                 // do not log nor dispatch events for "ignored" players other than the release
371                 return;
372             }
373             sEventLogger.enqueue(new PlayerEvent(piid, event, eventValue));
374 
375             if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_PORT_ID) {
376                 if (portToPiidSimplification()) {
377                     mPiidToPortId.put(piid, eventValue);
378                 } else {
379                     mPortIdToPiid.put(eventValue, piid);
380                 }
381                 return;
382             } else if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
383                 for (Integer uidInteger: mBannedUids) {
384                     if (checkBanPlayer(apc, uidInteger.intValue())) {
385                         // player was banned, do not update its state
386                         sEventLogger.enqueue(new EventLogger.StringEvent(
387                                 "not starting piid:" + piid + ", is banned"));
388                         return;
389                     }
390                 }
391             }
392             if (apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL
393                     && event != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) {
394                 // FIXME SoundPool not ready for state reporting
395                 return;
396             }
397             if (checkConfigurationCaller(piid, apc, binderUid)) {
398                 //TODO add generation counter to only update to the latest state
399                 checkVolumeForPrivilegedAlarm(apc, event);
400                 change = apc.handleStateEvent(event, eventValue);
401             } else {
402                 Log.e(TAG, "Error handling event " + event);
403                 change = false;
404             }
405             if (change) {
406                 if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
407                     mDuckingManager.checkDuck(apc);
408                     mFadeOutManager.checkFade(apc);
409                 }
410                 if (doNotLog) {
411                     // do not dispatch events for "ignored" players
412                     change = false;
413                 }
414             }
415         }
416         if (change) {
417             dispatchPlaybackChange(event == AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
418         }
419     }
420 
421     /**
422      * Update event for port
423      * @param portId Port id to update
424      * @param event The new port event
425      * @param extras The values associated with this event
426      * @param binderUid Calling binder uid
427      */
portEvent(int portId, int event, @Nullable PersistableBundle extras, int binderUid)428     public void portEvent(int portId, int event, @Nullable PersistableBundle extras,
429             int binderUid) {
430         if (!UserHandle.isCore(binderUid)) {
431             Log.e(TAG, "Forbidden operation from uid " + binderUid);
432             return;
433         }
434 
435         if (DEBUG) {
436             Log.v(TAG, TextUtils.formatSimple("BLA portEvent(portId=%d, event=%s, extras=%s)",
437                     portId, AudioPlaybackConfiguration.playerStateToString(event), extras));
438         }
439 
440         synchronized (mPlayerLock) {
441             int piid;
442             if (portToPiidSimplification()) {
443                 int idxOfPiid = mPiidToPortId.indexOfValue(portId);
444                 if (idxOfPiid < 0) {
445                     Log.w(TAG, "No piid assigned for invalid/internal port id " + portId);
446                     return;
447                 }
448                 piid = mPiidToPortId.keyAt(idxOfPiid);
449             } else {
450                 piid = mPortIdToPiid.get(portId, PLAYER_PIID_INVALID);
451                 if (piid == PLAYER_PIID_INVALID) {
452                     Log.w(TAG, "No piid assigned for invalid/internal port id " + portId);
453                     return;
454                 }
455             }
456             final AudioPlaybackConfiguration apc = mPlayers.get(piid);
457             if (apc == null) {
458                 Log.w(TAG, "No AudioPlaybackConfiguration assigned for piid " + piid);
459                 return;
460             }
461 
462             if (apc.getPlayerType()
463                     == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
464                 // FIXME SoundPool not ready for state reporting
465                 return;
466             }
467 
468             if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED) {
469                 mEventHandler.sendMessage(
470                         mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_MUTED_EVENT, piid,
471                                 portId,
472                                 extras));
473             } else if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_FORMAT) {
474                 mEventHandler.sendMessage(
475                         mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_FORMAT, piid,
476                                 portId,
477                                 extras));
478             }
479         }
480     }
481 
playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid)482     public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid) {
483         // no check on UID yet because this is only for logging at the moment
484         sEventLogger.enqueue(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid));
485     }
486 
releasePlayer(int piid, int binderUid)487     public void releasePlayer(int piid, int binderUid) {
488         if (DEBUG) { Log.v(TAG, "releasePlayer() for piid=" + piid); }
489         boolean change = false;
490         if (piid == PLAYER_PIID_INVALID) {
491             Log.w(TAG, "Received releasePlayer with invalid piid: " + piid);
492             sEventLogger.enqueue(new EventLogger.StringEvent("releasePlayer with invalid piid:"
493                     + piid + ", uid:" + binderUid));
494             return;
495         }
496 
497         synchronized(mPlayerLock) {
498             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
499             if (checkConfigurationCaller(piid, apc, binderUid)) {
500                 sEventLogger.enqueue(new EventLogger.StringEvent(
501                         "releasing player piid:" + piid + ", uid:" + binderUid));
502                 mPlayers.remove(new Integer(piid));
503                 mDuckingManager.removeReleased(apc);
504                 mFadeOutManager.removeReleased(apc);
505                 mMutedPlayersAwaitingConnection.remove(Integer.valueOf(piid));
506                 checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
507                 change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED,
508                         AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID);
509 
510                 if (portToPiidSimplification()) {
511                     mPiidToPortId.delete(piid);
512                 } else {
513                     // remove all port ids mapped to the released player
514                     int portIdx;
515                     while ((portIdx = mPortIdToPiid.indexOfValue(piid)) >= 0) {
516                         mPortIdToPiid.removeAt(portIdx);
517                     }
518                 }
519 
520                 if (change && mDoNotLogPiidList.contains(piid)) {
521                     // do not dispatch a change for a "do not log" player
522                     change = false;
523                 }
524             }
525         }
526         if (change) {
527             dispatchPlaybackChange(true /*iplayerreleased*/);
528         }
529     }
530 
onAudioServerDied()531     /*package*/ void onAudioServerDied() {
532         sEventLogger.enqueue(
533                 new EventLogger.StringEvent(
534                         "clear port id to piid map"));
535         synchronized (mPlayerLock) {
536             if (portToPiidSimplification()) {
537                 if (DEBUG) {
538                     Log.v(TAG, "clear piid to portId map:\n" + mPiidToPortId);
539                 }
540                 mPiidToPortId.clear();
541             } else {
542                 if (DEBUG) {
543                     Log.v(TAG, "clear port id to piid map:\n" + mPortIdToPiid);
544                 }
545                 mPortIdToPiid.clear();
546             }
547         }
548     }
549 
550     /**
551      * A map of uid to capture policy.
552      */
553     private final HashMap<Integer, Integer> mAllowedCapturePolicies =
554             new HashMap<Integer, Integer>();
555 
556     /**
557      * Cache allowed capture policy, which specifies whether the audio played by the app may or may
558      * not be captured by other apps or the system.
559      *
560      * @param uid the uid of requested app
561      * @param capturePolicy one of
562      *     {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL},
563      *     {@link AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM},
564      *     {@link AudioAttributes#ALLOW_CAPTURE_BY_NONE}.
565      */
setAllowedCapturePolicy(int uid, int capturePolicy)566     public void setAllowedCapturePolicy(int uid, int capturePolicy) {
567         synchronized (mAllowedCapturePolicies) {
568             if (capturePolicy == AudioAttributes.ALLOW_CAPTURE_BY_ALL) {
569                 // When the capture policy is ALLOW_CAPTURE_BY_ALL, it is okay to
570                 // remove it from cached capture policy as it is the default value.
571                 mAllowedCapturePolicies.remove(uid);
572                 return;
573             } else {
574                 mAllowedCapturePolicies.put(uid, capturePolicy);
575             }
576         }
577         synchronized (mPlayerLock) {
578             for (AudioPlaybackConfiguration apc : mPlayers.values()) {
579                 if (apc.getClientUid() == uid) {
580                     updateAllowedCapturePolicy(apc, capturePolicy);
581                 }
582             }
583         }
584     }
585 
586     /**
587      * Return the capture policy for given uid.
588      * @param uid the uid to query its cached capture policy.
589      * @return cached capture policy for given uid or AudioAttributes.ALLOW_CAPTURE_BY_ALL
590      *         if there is not cached capture policy.
591      */
getAllowedCapturePolicy(int uid)592     public int getAllowedCapturePolicy(int uid) {
593         return mAllowedCapturePolicies.getOrDefault(uid, AudioAttributes.ALLOW_CAPTURE_BY_ALL);
594     }
595 
596     /**
597      * Return a copy of all cached capture policies.
598      */
getAllAllowedCapturePolicies()599     public HashMap<Integer, Integer> getAllAllowedCapturePolicies() {
600         synchronized (mAllowedCapturePolicies) {
601             return (HashMap<Integer, Integer>) mAllowedCapturePolicies.clone();
602         }
603     }
604 
updateAllowedCapturePolicy(AudioPlaybackConfiguration apc, int capturePolicy)605     private void updateAllowedCapturePolicy(AudioPlaybackConfiguration apc, int capturePolicy) {
606         AudioAttributes attr = apc.getAudioAttributes();
607         if (attr.getAllowedCapturePolicy() >= capturePolicy) {
608             return;
609         }
610         apc.handleAudioAttributesEvent(
611                 new AudioAttributes.Builder(apc.getAudioAttributes())
612                         .setAllowedCapturePolicy(capturePolicy).build());
613     }
614 
615     // Implementation of AudioPlaybackConfiguration.PlayerDeathMonitor
616     @Override
playerDeath(int piid)617     public void playerDeath(int piid) {
618         releasePlayer(piid, 0);
619     }
620 
621     /**
622      * Returns true if a player belonging to the app with given uid is active.
623      *
624      * @param uid the app uid
625      * @return true if a player is active, false otherwise
626      */
isPlaybackActiveForUid(int uid)627     public boolean isPlaybackActiveForUid(int uid) {
628         synchronized (mPlayerLock) {
629             for (AudioPlaybackConfiguration apc : mPlayers.values()) {
630                 if (apc.isActive() && apc.getClientUid() == uid) {
631                     return true;
632                 }
633             }
634         }
635         return false;
636     }
637 
638     /**
639      * Return true if an active playback for media use case is currently routed to
640      * a remote submix device with the supplied address.
641      * @param address
642      */
hasActiveMediaPlaybackOnSubmixWithAddress(@onNull String address)643     public boolean hasActiveMediaPlaybackOnSubmixWithAddress(@NonNull String address) {
644         synchronized (mPlayerLock) {
645             for (AudioPlaybackConfiguration apc : mPlayers.values()) {
646                 AudioDeviceInfo device = apc.getAudioDeviceInfo();
647                 if (apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_MEDIA
648                         && apc.isActive() && device != null
649                         && device.getInternalType() == AudioSystem.DEVICE_OUT_REMOTE_SUBMIX
650                         && address.equals(device.getAddress())) {
651                     return true;
652                 }
653             }
654         }
655         return false;
656     }
657 
dump(PrintWriter pw)658     protected void dump(PrintWriter pw) {
659         // players
660         pw.println("\nPlaybackActivityMonitor dump time: "
661                 + DateFormat.getTimeInstance().format(new Date()));
662         synchronized(mPlayerLock) {
663             pw.println("\n  playback listeners:");
664             for (PlayMonitorClient pmc : mClients) {
665                 pw.print(" " + (pmc.isPrivileged() ? "(S)" : "(P)")
666                         + pmc.toString());
667             }
668             pw.println("\n");
669             // all players
670             pw.println("\n  players:");
671             final List<Integer> piidIntList = new ArrayList<Integer>(mPlayers.keySet());
672             Collections.sort(piidIntList);
673             for (Integer piidInt : piidIntList) {
674                 final AudioPlaybackConfiguration apc = mPlayers.get(piidInt);
675                 if (apc != null) {
676                     if (mDoNotLogPiidList.contains(apc.getPlayerInterfaceId())) {
677                         pw.print("(not logged)");
678                     }
679                     apc.dump(pw);
680                 }
681             }
682             // ducked players
683             pw.println("\n  ducked players piids:");
684             mDuckingManager.dump(pw);
685             // faded out players
686             pw.println("\n  faded out players piids:");
687             mFadeOutManager.dump(pw);
688             // players muted due to the device ringing or being in a call
689             pw.print("\n  muted player piids due to call/ring:");
690             for (int piid : mMutedPlayers) {
691                 pw.print(" " + piid);
692             }
693             pw.println();
694             // banned players:
695             pw.print("\n  banned uids:");
696             for (int uid : mBannedUids) {
697                 pw.print(" " + uid);
698             }
699             pw.println("\n");
700             // muted players:
701             pw.print("\n  muted players (piids) awaiting device connection:");
702             for (int piid : mMutedPlayersAwaitingConnection) {
703                 pw.print(" " + piid);
704             }
705             pw.println("\n");
706             if (portToPiidSimplification()) {
707                 // portId to piid mappings:
708                 pw.println("\n  current piid to portId map:");
709                 for (int i = 0; i < mPiidToPortId.size(); ++i) {
710                     pw.println(
711                             "  piid: " + mPiidToPortId.keyAt(i) + " portId: "
712                                     + mPiidToPortId.valueAt(i));
713                 }
714             } else {
715                 // portId to piid mappings:
716                 pw.println("\n  current portId to piid map:");
717                 for (int i = 0; i < mPortIdToPiid.size(); ++i) {
718                     pw.println("  portId: " + mPortIdToPiid.keyAt(i) + " piid: "
719                             + mPortIdToPiid.valueAt(i));
720                 }
721             }
722             pw.println("\n");
723             // log
724             sEventLogger.dump(pw);
725         }
726 
727         synchronized (mAllowedCapturePolicies) {
728             pw.println("\n  allowed capture policies:");
729             for (HashMap.Entry<Integer, Integer> entry : mAllowedCapturePolicies.entrySet()) {
730                 pw.println("  uid: " + entry.getKey() + " policy: " + entry.getValue());
731             }
732         }
733     }
734 
735     /**
736      * Check that piid and uid are valid for the given valid configuration.
737      * @param piid the piid of the player.
738      * @param apc the configuration found for this piid.
739      * @param binderUid actual uid of client trying to signal a player state/event/attributes.
740      * @return true if the call is valid and the change should proceed, false otherwise. Always
741      *      returns false when apc is null.
742      */
checkConfigurationCaller(int piid, final AudioPlaybackConfiguration apc, int binderUid)743     private static boolean checkConfigurationCaller(int piid,
744             final AudioPlaybackConfiguration apc, int binderUid) {
745         if (apc == null) {
746             return false;
747         } else if ((binderUid != 0) && (apc.getClientUid() != binderUid)) {
748             Log.e(TAG, "Forbidden operation from uid " + binderUid + " for player " + piid);
749             return false;
750         }
751         return true;
752     }
753 
754     /**
755      * Sends new list after update of playback configurations
756      * @param iplayerReleased indicates if the change was due to a player being released
757      */
dispatchPlaybackChange(boolean iplayerReleased)758     private void dispatchPlaybackChange(boolean iplayerReleased) {
759         if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); }
760         final List<AudioPlaybackConfiguration> configsSystem;
761         // list of playback configurations for "public consumption". It is computed lazy if there
762         // are non-system playback activity listeners.
763         List<AudioPlaybackConfiguration> configsPublic = null;
764         synchronized (mPlayerLock) {
765             if (mPlayers.isEmpty()) {
766                 return;
767             }
768             configsSystem = new ArrayList<>(mPlayers.values());
769         }
770 
771         final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
772         while (clientIterator.hasNext()) {
773             final PlayMonitorClient pmc = clientIterator.next();
774             // do not spam the logs if there are problems communicating with this client
775             if (!pmc.reachedMaxErrorCount()) {
776                 if (pmc.isPrivileged()) {
777                     pmc.dispatchPlaybackConfigChange(configsSystem,
778                             iplayerReleased);
779                 } else {
780                     if (configsPublic == null) {
781                         configsPublic = anonymizeForPublicConsumption(configsSystem);
782                     }
783                     // non-system clients don't have the control interface IPlayer, so
784                     // they don't need to flush commands when a player was released
785                     pmc.dispatchPlaybackConfigChange(configsPublic, false);
786                 }
787             }
788         }
789     }
790 
anonymizeForPublicConsumption( List<AudioPlaybackConfiguration> sysConfigs)791     private ArrayList<AudioPlaybackConfiguration> anonymizeForPublicConsumption(
792             List<AudioPlaybackConfiguration> sysConfigs) {
793         ArrayList<AudioPlaybackConfiguration> publicConfigs =
794                 new ArrayList<AudioPlaybackConfiguration>();
795         // only add active anonymized configurations,
796         for (AudioPlaybackConfiguration config : sysConfigs) {
797             if (config.isActive()) {
798                 publicConfigs.add(AudioPlaybackConfiguration.anonymizedCopy(config));
799             }
800         }
801         return publicConfigs;
802     }
803 
804 
805     //=================================================================
806     // PlayerFocusEnforcer implementation
807     private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();
808 
809     private final DuckingManager mDuckingManager = new DuckingManager();
810 
811     @Override
duckPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser, boolean forceDuck)812     public boolean duckPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser,
813                                boolean forceDuck) {
814         if (DEBUG) {
815             Log.v(TAG, String.format("duckPlayers: uids winner=%d loser=%d",
816                     winner.getClientUid(), loser.getClientUid()));
817         }
818         synchronized (mPlayerLock) {
819             if (mPlayers.isEmpty()) {
820                 return true;
821             }
822             // check if this UID needs to be ducked (return false if not), and gather list of
823             // eligible players to duck
824             final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator();
825             final ArrayList<AudioPlaybackConfiguration> apcsToDuck =
826                     new ArrayList<AudioPlaybackConfiguration>();
827             while (apcIterator.hasNext()) {
828                 final AudioPlaybackConfiguration apc = apcIterator.next();
829                 if (!winner.hasSameUid(apc.getClientUid())
830                         && loser.hasSameUid(apc.getClientUid())
831                         && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED)
832                 {
833                     if (!forceDuck && (apc.getAudioAttributes().getContentType() ==
834                             AudioAttributes.CONTENT_TYPE_SPEECH)) {
835                         // the player is speaking, ducking will make the speech unintelligible
836                         // so let the app handle it instead
837                         Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
838                                 + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
839                                 + " - SPEECH");
840                         return false;
841                     } else if (ArrayUtils.contains(UNDUCKABLE_PLAYER_TYPES, apc.getPlayerType())) {
842                         Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
843                                 + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
844                                 + " due to type:"
845                                 + AudioPlaybackConfiguration.toLogFriendlyPlayerType(
846                                         apc.getPlayerType()));
847                         return false;
848                     }
849                     apcsToDuck.add(apc);
850                 }
851             }
852             // add the players eligible for ducking to the list, and duck them
853             // (if apcsToDuck is empty, this will at least mark this uid as ducked, so when
854             //  players of the same uid start, they will be ducked by DuckingManager.checkDuck())
855             mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck, reqCausesStrongDuck(winner));
856         }
857         return true;
858     }
859 
reqCausesStrongDuck(FocusRequester requester)860     private boolean reqCausesStrongDuck(FocusRequester requester) {
861         if (requester.getGainRequest() != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) {
862             return false;
863         }
864         final int reqUsage = requester.getAudioAttributes().getUsage();
865         if (reqUsage == AudioAttributes.USAGE_ASSISTANT) {
866             return true;
867         }
868         return false;
869     }
870 
871     @Override
restoreVShapedPlayers(@onNull FocusRequester winner)872     public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
873         if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); }
874         synchronized (mPlayerLock) {
875             mDuckingManager.unduckUid(winner.getClientUid(), mPlayers);
876             mFadeOutManager.unfadeOutUid(winner.getClientUid(), mPlayers);
877         }
878     }
879 
880     @Override
mutePlayersForCall(int[] usagesToMute)881     public void mutePlayersForCall(int[] usagesToMute) {
882         if (DEBUG) {
883             String log = new String("mutePlayersForCall: usages=");
884             for (int usage : usagesToMute) { log += " " + usage; }
885             Log.v(TAG, log);
886         }
887         synchronized (mPlayerLock) {
888             final Set<Integer> piidSet = mPlayers.keySet();
889             final Iterator<Integer> piidIterator = piidSet.iterator();
890             // find which players to mute
891             while (piidIterator.hasNext()) {
892                 final Integer piid = piidIterator.next();
893                 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
894                 if (apc == null) {
895                     continue;
896                 }
897                 final int playerUsage = apc.getAudioAttributes().getUsage();
898                 boolean mute = false;
899                 for (int usageToMute : usagesToMute) {
900                     if (playerUsage == usageToMute) {
901                         mute = true;
902                         break;
903                     }
904                 }
905                 if (mute) {
906                     try {
907                         sEventLogger.enqueue((new EventLogger.StringEvent("call: muting piid:"
908                                 + piid + " uid:" + apc.getClientUid())).printLog(TAG));
909                         apc.getPlayerProxy().setVolume(0.0f);
910                         mMutedPlayers.add(new Integer(piid));
911                     } catch (Exception e) {
912                         Log.e(TAG, "call: error muting player " + piid, e);
913                     }
914                 }
915             }
916         }
917     }
918 
919     @Override
unmutePlayersForCall()920     public void unmutePlayersForCall() {
921         if (DEBUG) {
922             Log.v(TAG, "unmutePlayersForCall()");
923         }
924         synchronized (mPlayerLock) {
925             if (mMutedPlayers.isEmpty()) {
926                 return;
927             }
928             for (int piid : mMutedPlayers) {
929                 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
930                 if (apc != null) {
931                     try {
932                         sEventLogger.enqueue(new EventLogger.StringEvent("call: unmuting piid:"
933                                 + piid).printLog(TAG));
934                         apc.getPlayerProxy().setVolume(1.0f);
935                     } catch (Exception e) {
936                         Log.e(TAG, "call: error unmuting player " + piid + " uid:"
937                                 + apc.getClientUid(), e);
938                     }
939                 }
940             }
941             mMutedPlayers.clear();
942         }
943     }
944 
945     /**
946      *
947      * @param winner the new non-transient focus owner
948      * @param loser the previous focus owner
949      * @return true if there are players being faded out
950      */
951     @Override
fadeOutPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser)952     public boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser) {
953         if (DEBUG) {
954             Log.v(TAG, "fadeOutPlayers: winner=" + winner.getPackageName()
955                     +  " loser=" + loser.getPackageName());
956         }
957         boolean loserHasActivePlayers = false;
958 
959         // find which players to fade out
960         synchronized (mPlayerLock) {
961             if (mPlayers.isEmpty()) {
962                 if (DEBUG) { Log.v(TAG, "no players to fade out"); }
963                 return false;
964             }
965             if (!mFadeOutManager.canCauseFadeOut(winner, loser)) {
966                 return false;
967             }
968             // check if this UID needs to be faded out (return false if not), and gather list of
969             // eligible players to fade out
970             final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator();
971             final ArrayList<AudioPlaybackConfiguration> apcsToFadeOut =
972                     new ArrayList<AudioPlaybackConfiguration>();
973             while (apcIterator.hasNext()) {
974                 final AudioPlaybackConfiguration apc = apcIterator.next();
975                 if (!winner.hasSameUid(apc.getClientUid())
976                         && loser.hasSameUid(apc.getClientUid())
977                         && apc.getPlayerState()
978                         == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
979                     if (!mFadeOutManager.canBeFadedOut(apc)) {
980                         // the player is not eligible to be faded out, bail
981                         Log.v(TAG, "not fading out player " + apc.getPlayerInterfaceId()
982                                 + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
983                                 + " type:"
984                                 + AudioPlaybackConfiguration.toLogFriendlyPlayerType(
985                                         apc.getPlayerType())
986                                 + " attr:" + apc.getAudioAttributes());
987                         return false;
988                     }
989                     loserHasActivePlayers = true;
990                     apcsToFadeOut.add(apc);
991                 }
992             }
993             if (loserHasActivePlayers) {
994                 mFadeOutManager.fadeOutUid(loser.getClientUid(), apcsToFadeOut);
995             }
996         }
997 
998         return loserHasActivePlayers;
999     }
1000 
1001     @Override
forgetUid(int uid)1002     public void forgetUid(int uid) {
1003         final HashMap<Integer, AudioPlaybackConfiguration> players;
1004         synchronized (mPlayerLock) {
1005             players = (HashMap<Integer, AudioPlaybackConfiguration>) mPlayers.clone();
1006         }
1007         mFadeOutManager.unfadeOutUid(uid, players);
1008         mDuckingManager.unduckUid(uid, players);
1009     }
1010 
1011     @Override
getFadeOutDurationMillis(@onNull AudioAttributes aa)1012     public long getFadeOutDurationMillis(@NonNull AudioAttributes aa) {
1013         return mFadeOutManager.getFadeOutDurationOnFocusLossMillis(aa);
1014     }
1015 
1016     @Override
getFadeInDelayForOffendersMillis(@onNull AudioAttributes aa)1017     public long getFadeInDelayForOffendersMillis(@NonNull AudioAttributes aa) {
1018         return mFadeOutManager.getFadeInDelayForOffendersMillis(aa);
1019     }
1020 
1021     @Override
shouldEnforceFade()1022     public boolean shouldEnforceFade() {
1023         return mFadeOutManager.isFadeEnabled();
1024     }
1025 
1026 
1027     //=================================================================
1028     // Track playback activity listeners
1029 
registerPlaybackCallback(IPlaybackConfigDispatcher pcdb, boolean isPrivileged)1030     void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
1031         if (pcdb == null) {
1032             return;
1033         }
1034         final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
1035         if (pmc.init()) {
1036             mClients.add(pmc);
1037         }
1038     }
1039 
unregisterPlaybackCallback(IPlaybackConfigDispatcher pcdb)1040     void unregisterPlaybackCallback(IPlaybackConfigDispatcher pcdb) {
1041         if (pcdb == null) {
1042             return;
1043         }
1044         final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
1045         // iterate over the clients to remove the dispatcher
1046         while (clientIterator.hasNext()) {
1047             PlayMonitorClient pmc = clientIterator.next();
1048             if (pmc.equalsDispatcher(pcdb)) {
1049                 pmc.release();
1050                 clientIterator.remove();
1051             }
1052         }
1053     }
1054 
getActivePlaybackConfigurations(boolean isPrivileged)1055     List<AudioPlaybackConfiguration> getActivePlaybackConfigurations(boolean isPrivileged) {
1056         synchronized (mPlayerLock) {
1057             if (isPrivileged) {
1058                 return new ArrayList<AudioPlaybackConfiguration>(mPlayers.values());
1059             } else {
1060                 return anonymizeForPublicConsumption(
1061                             new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()));
1062             }
1063         }
1064     }
1065 
setFadeManagerConfiguration(int focusType, FadeManagerConfiguration fadeMgrConfig)1066     int setFadeManagerConfiguration(int focusType, FadeManagerConfiguration fadeMgrConfig) {
1067         return mFadeOutManager.setFadeManagerConfiguration(fadeMgrConfig);
1068     }
1069 
clearFadeManagerConfiguration(int focusType)1070     int clearFadeManagerConfiguration(int focusType) {
1071         return mFadeOutManager.clearFadeManagerConfiguration();
1072     }
1073 
getFadeManagerConfiguration(int focusType)1074     FadeManagerConfiguration getFadeManagerConfiguration(int focusType) {
1075         return mFadeOutManager.getFadeManagerConfiguration();
1076     }
1077 
setTransientFadeManagerConfiguration(int focusType, FadeManagerConfiguration fadeMgrConfig)1078     int setTransientFadeManagerConfiguration(int focusType,
1079             FadeManagerConfiguration fadeMgrConfig) {
1080         return mFadeOutManager.setTransientFadeManagerConfiguration(fadeMgrConfig);
1081     }
1082 
clearTransientFadeManagerConfiguration(int focusType)1083     int clearTransientFadeManagerConfiguration(int focusType) {
1084         return mFadeOutManager.clearTransientFadeManagerConfiguration();
1085     }
1086 
1087     /**
1088      * Inner class to track clients that want to be notified of playback updates
1089      */
1090     private static final class PlayMonitorClient implements IBinder.DeathRecipient {
1091 
1092         // can afford to be static because only one PlaybackActivityMonitor ever instantiated
1093         static PlaybackActivityMonitor sListenerDeathMonitor;
1094 
1095         // number of errors after which we don't update this client anymore to not spam the logs
1096         private static final int MAX_ERRORS = 5;
1097 
1098         private final IPlaybackConfigDispatcher mDispatcherCb;
1099 
1100         @GuardedBy("this")
1101         private final boolean mIsPrivileged;
1102         @GuardedBy("this")
1103         private boolean mIsReleased = false;
1104         @GuardedBy("this")
1105         private int mErrorCount = 0;
1106 
PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged)1107         PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
1108             mDispatcherCb = pcdb;
1109             mIsPrivileged = isPrivileged;
1110         }
1111 
1112         @Override
binderDied()1113         public void binderDied() {
1114             Log.w(TAG, "client died");
1115             sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb);
1116         }
1117 
init()1118         synchronized boolean init() {
1119             if (mIsReleased) {
1120                 // Do not init after release
1121                 return false;
1122             }
1123             try {
1124                 mDispatcherCb.asBinder().linkToDeath(this, 0);
1125                 return true;
1126             } catch (RemoteException e) {
1127                 Log.w(TAG, "Could not link to client death", e);
1128                 return false;
1129             }
1130         }
1131 
release()1132         synchronized void release() {
1133             mDispatcherCb.asBinder().unlinkToDeath(this, 0);
1134             mIsReleased = true;
1135         }
1136 
dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs, boolean flush)1137         void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
1138                 boolean flush) {
1139             synchronized (this) {
1140                 if (mIsReleased) {
1141                     // Do not dispatch anything after release
1142                     return;
1143                 }
1144             }
1145             try {
1146                 mDispatcherCb.dispatchPlaybackConfigChange(configs, flush);
1147             } catch (RemoteException e) {
1148                 synchronized (this) {
1149                     mErrorCount++;
1150                     Log.e(TAG, "Error (" + mErrorCount
1151                             + ") trying to dispatch playback config change to " + this, e);
1152                 }
1153             }
1154         }
1155 
isPrivileged()1156         synchronized boolean isPrivileged() {
1157             return mIsPrivileged;
1158         }
1159 
reachedMaxErrorCount()1160         synchronized boolean reachedMaxErrorCount() {
1161             return mErrorCount >= MAX_ERRORS;
1162         }
1163 
equalsDispatcher(IPlaybackConfigDispatcher pcdb)1164         synchronized boolean equalsDispatcher(IPlaybackConfigDispatcher pcdb) {
1165             if (pcdb == null) {
1166                 return false;
1167             }
1168             return pcdb.asBinder().equals(mDispatcherCb.asBinder());
1169         }
1170     }
1171 
1172     //=================================================================
1173     // Class to handle ducking related operations for a given UID
1174     private static final class DuckingManager {
1175         private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>();
1176 
duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck, boolean requestCausesStrongDuck)1177         synchronized void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck,
1178                 boolean requestCausesStrongDuck) {
1179             if (DEBUG) {  Log.v(TAG, "DuckingManager: duckUid() uid:"+ uid); }
1180             if (!mDuckers.containsKey(uid)) {
1181                 mDuckers.put(uid, new DuckedApp(uid, requestCausesStrongDuck));
1182             }
1183             final DuckedApp da = mDuckers.get(uid);
1184             for (AudioPlaybackConfiguration apc : apcsToDuck) {
1185                 da.addDuck(apc, false /*skipRamp*/);
1186             }
1187         }
1188 
unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players)1189         synchronized void unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
1190             if (DEBUG) {  Log.v(TAG, "DuckingManager: unduckUid() uid:"+ uid); }
1191             final DuckedApp da = mDuckers.remove(uid);
1192             if (da == null) {
1193                 return;
1194             }
1195             da.removeUnduckAll(players);
1196         }
1197 
1198         // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
checkDuck(@onNull AudioPlaybackConfiguration apc)1199         synchronized void checkDuck(@NonNull AudioPlaybackConfiguration apc) {
1200             if (DEBUG) {  Log.v(TAG, "DuckingManager: checkDuck() player piid:"
1201                     + apc.getPlayerInterfaceId()+ " uid:"+ apc.getClientUid()); }
1202             final DuckedApp da = mDuckers.get(apc.getClientUid());
1203             if (da == null) {
1204                 return;
1205             }
1206             da.addDuck(apc, true /*skipRamp*/);
1207         }
1208 
dump(PrintWriter pw)1209         synchronized void dump(PrintWriter pw) {
1210             for (DuckedApp da : mDuckers.values()) {
1211                 da.dump(pw);
1212             }
1213         }
1214 
removeReleased(@onNull AudioPlaybackConfiguration apc)1215         synchronized void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
1216             final int uid = apc.getClientUid();
1217             if (DEBUG) {  Log.v(TAG, "DuckingManager: removedReleased() player piid: "
1218                     + apc.getPlayerInterfaceId() + " uid:" + uid); }
1219             final DuckedApp da = mDuckers.get(uid);
1220             if (da == null) {
1221                 return;
1222             }
1223             da.removeReleased(apc);
1224         }
1225 
1226         private static final class DuckedApp {
1227             private final int mUid;
1228             /** determines whether ducking is done with DUCK_VSHAPE or STRONG_DUCK_VSHAPE */
1229             private final boolean mUseStrongDuck;
1230             private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
1231 
DuckedApp(int uid, boolean useStrongDuck)1232             DuckedApp(int uid, boolean useStrongDuck) {
1233                 mUid = uid;
1234                 mUseStrongDuck = useStrongDuck;
1235             }
1236 
dump(PrintWriter pw)1237             void dump(PrintWriter pw) {
1238                 pw.print("\t uid:" + mUid + " piids:");
1239                 for (int piid : mDuckedPlayers) {
1240                     pw.print(" " + piid);
1241                 }
1242                 pw.println("");
1243             }
1244 
1245             // pre-conditions:
1246             //  * apc != null
1247             //  * apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
addDuck(@onNull AudioPlaybackConfiguration apc, boolean skipRamp)1248             void addDuck(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
1249                 final int piid = new Integer(apc.getPlayerInterfaceId());
1250                 if (mDuckedPlayers.contains(piid)) {
1251                     if (DEBUG) { Log.v(TAG, "player piid:" + piid + " already ducked"); }
1252                     return;
1253                 }
1254                 try {
1255                     VolumeShaper.Configuration config =
1256                             mUseStrongDuck ? STRONG_DUCK_VSHAPE : DUCK_VSHAPE;
1257                     VolumeShaper.Operation operation =
1258                             skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED;
1259                     sEventLogger.enqueue((new DuckEvent(apc, skipRamp, mUseStrongDuck, config,
1260                             operation)).printLog(TAG));
1261                     apc.getPlayerProxy().applyVolumeShaper(config, operation);
1262                     mDuckedPlayers.add(piid);
1263                 } catch (Exception e) {
1264                     Log.e(TAG, "Error ducking player piid:" + piid + " uid:" + mUid, e);
1265                 }
1266             }
1267 
removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players)1268             void removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players) {
1269                 for (int piid : mDuckedPlayers) {
1270                     final AudioPlaybackConfiguration apc = players.get(piid);
1271                     if (apc != null) {
1272                         try {
1273                             sEventLogger.enqueue((new EventLogger.StringEvent("unducking piid:"
1274                                     + piid)).printLog(TAG));
1275                             apc.getPlayerProxy().applyVolumeShaper(
1276                                     mUseStrongDuck ? STRONG_DUCK_ID : DUCK_ID,
1277                                     VolumeShaper.Operation.REVERSE);
1278                         } catch (Exception e) {
1279                             Log.e(TAG, "Error unducking player piid:" + piid + " uid:" + mUid, e);
1280                         }
1281                     } else {
1282                         // this piid was in the list of ducked players, but wasn't found
1283                         if (DEBUG) {
1284                             Log.v(TAG, "Error unducking player piid:" + piid
1285                                     + ", player not found for uid " + mUid);
1286                         }
1287                     }
1288                 }
1289                 mDuckedPlayers.clear();
1290             }
1291 
removeReleased(@onNull AudioPlaybackConfiguration apc)1292             void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
1293                 mDuckedPlayers.remove(new Integer(apc.getPlayerInterfaceId()));
1294             }
1295         }
1296     }
1297 
getFocusDuckedUids()1298     protected @NonNull List<Integer> getFocusDuckedUids() {
1299         final ArrayList<Integer> duckedUids;
1300         synchronized (mPlayerLock) {
1301             duckedUids = new ArrayList(mDuckingManager.mDuckers.keySet());
1302         }
1303         if (DEBUG) {
1304             Log.i(TAG, "current ducked UIDs: " + duckedUids);
1305         }
1306         return duckedUids;
1307     }
1308 
1309     //=================================================================
1310     // For logging
1311     private static final class PlayerEvent extends EventLogger.Event {
1312         // only keeping the player interface ID as it uniquely identifies the player in the event
1313         final int mPlayerIId;
1314         final int mEvent;
1315         final int mEventValue;
1316 
PlayerEvent(int piid, int event, int eventValue)1317         PlayerEvent(int piid, int event, int eventValue) {
1318             mPlayerIId = piid;
1319             mEvent = event;
1320             mEventValue = eventValue;
1321         }
1322 
1323         @Override
eventToString()1324         public String eventToString() {
1325             StringBuilder builder = new StringBuilder("player piid:").append(mPlayerIId).append(
1326                             " event:")
1327                     .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mEvent));
1328 
1329             switch (mEvent) {
1330                 case AudioPlaybackConfiguration.PLAYER_UPDATE_PORT_ID:
1331                     return AudioPlaybackConfiguration.toLogFriendlyPlayerState(mEvent) + " portId:"
1332                             + mEventValue + " mapped to player piid:" + mPlayerIId;
1333                 case AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID:
1334                     if (mEventValue != 0) {
1335                         builder.append(" deviceId:").append(mEventValue);
1336                     }
1337                     return builder.toString();
1338                 case AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED:
1339                     builder.append(" source:");
1340                     if (mEventValue <= 0) {
1341                         builder.append("none ");
1342                     } else {
1343                         if ((mEventValue & MUTED_BY_MASTER) != 0) {
1344                             builder.append("masterMute ");
1345                         }
1346                         if ((mEventValue & MUTED_BY_STREAM_VOLUME) != 0) {
1347                             builder.append("streamVolume ");
1348                         }
1349                         if ((mEventValue & MUTED_BY_STREAM_MUTED) != 0) {
1350                             builder.append("streamMute ");
1351                         }
1352                         if ((mEventValue & MUTED_BY_APP_OPS) != 0) {
1353                             builder.append("appOps ");
1354                         }
1355                         if ((mEventValue & MUTED_BY_CLIENT_VOLUME) != 0) {
1356                             builder.append("clientVolume ");
1357                         }
1358                         if ((mEventValue & MUTED_BY_VOLUME_SHAPER) != 0) {
1359                             builder.append("volumeShaper ");
1360                         }
1361                     }
1362                     return builder.toString();
1363                 default:
1364                     return builder.toString();
1365             }
1366         }
1367     }
1368 
1369     private static final class PlayerOpPlayAudioEvent extends EventLogger.Event {
1370         // only keeping the player interface ID as it uniquely identifies the player in the event
1371         final int mPlayerIId;
1372         final boolean mHasOp;
1373         final int mUid;
1374 
PlayerOpPlayAudioEvent(int piid, boolean hasOp, int uid)1375         PlayerOpPlayAudioEvent(int piid, boolean hasOp, int uid) {
1376             mPlayerIId = piid;
1377             mHasOp = hasOp;
1378             mUid = uid;
1379         }
1380 
1381         @Override
eventToString()1382         public String eventToString() {
1383             return new StringBuilder("player piid:").append(mPlayerIId)
1384                     .append(" has OP_PLAY_AUDIO:").append(mHasOp)
1385                     .append(" in uid:").append(mUid).toString();
1386         }
1387     }
1388 
1389     private static final class NewPlayerEvent extends EventLogger.Event {
1390         private final int mPlayerIId;
1391         private final int mPlayerType;
1392         private final int mClientUid;
1393         private final int mClientPid;
1394         private final AudioAttributes mPlayerAttr;
1395         private final int mSessionId;
1396 
NewPlayerEvent(AudioPlaybackConfiguration apc)1397         NewPlayerEvent(AudioPlaybackConfiguration apc) {
1398             mPlayerIId = apc.getPlayerInterfaceId();
1399             mPlayerType = apc.getPlayerType();
1400             mClientUid = apc.getClientUid();
1401             mClientPid = apc.getClientPid();
1402             mPlayerAttr = apc.getAudioAttributes();
1403             mSessionId = apc.getSessionId();
1404         }
1405 
1406         @Override
eventToString()1407         public String eventToString() {
1408             return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/"
1409                     + mClientPid + " type:"
1410                     + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
1411                     + " attr:" + mPlayerAttr
1412                     + " session:" + mSessionId);
1413         }
1414     }
1415 
1416     private abstract static class VolumeShaperEvent extends EventLogger.Event {
1417         private final int mPlayerIId;
1418         private final boolean mSkipRamp;
1419         private final int mClientUid;
1420         private final int mClientPid;
1421         private final int mPlayerType;
1422         private final AudioAttributes mPlayerAttr;
1423         private final VolumeShaper.Configuration mConfig;
1424         private final VolumeShaper.Operation mOperation;
1425 
getVSAction()1426         abstract String getVSAction();
1427 
VolumeShaperEvent(@onNull AudioPlaybackConfiguration apc, boolean skipRamp, VolumeShaper.Configuration config, VolumeShaper.Operation operation)1428         VolumeShaperEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp,
1429                 VolumeShaper.Configuration config, VolumeShaper.Operation operation) {
1430             mPlayerIId = apc.getPlayerInterfaceId();
1431             mSkipRamp = skipRamp;
1432             mClientUid = apc.getClientUid();
1433             mClientPid = apc.getClientPid();
1434             mPlayerAttr = apc.getAudioAttributes();
1435             mPlayerType = apc.getPlayerType();
1436             mConfig = config;
1437             mOperation = operation;
1438         }
1439 
1440         @Override
eventToString()1441         public String eventToString() {
1442             return getVSAction()
1443                     + " player piid:" + mPlayerIId
1444                     + " uid/pid:" + mClientUid + "/" + mClientPid
1445                     + " skip ramp:" + mSkipRamp
1446                     + " player type:"
1447                     + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
1448                     + " attr:" + mPlayerAttr
1449                     + " config:" + mConfig
1450                     + " operation:" + mOperation;
1451         }
1452     }
1453 
1454     static final class DuckEvent extends VolumeShaperEvent {
1455         final boolean mUseStrongDuck;
1456 
1457         @Override
getVSAction()1458         String getVSAction() {
1459             return mUseStrongDuck ? "ducking (strong)" : "ducking";
1460         }
1461 
DuckEvent(@onNull AudioPlaybackConfiguration apc, boolean skipRamp, boolean useStrongDuck, VolumeShaper.Configuration config, VolumeShaper.Operation operation)1462         DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp, boolean useStrongDuck,
1463                 VolumeShaper.Configuration config, VolumeShaper.Operation operation)
1464         {
1465             super(apc, skipRamp, config, operation);
1466             mUseStrongDuck = useStrongDuck;
1467         }
1468     }
1469 
1470     static final class FadeOutEvent extends VolumeShaperEvent {
1471         @Override
getVSAction()1472         String getVSAction() {
1473             return EVENT_TYPE_FADE_OUT;
1474         }
1475 
FadeOutEvent(@onNull AudioPlaybackConfiguration apc, boolean skipRamp, VolumeShaper.Configuration config, VolumeShaper.Operation operation)1476         FadeOutEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp,
1477                 VolumeShaper.Configuration config, VolumeShaper.Operation operation) {
1478             super(apc, skipRamp, config, operation);
1479         }
1480     }
1481 
1482     static final class FadeInEvent extends VolumeShaperEvent {
1483         @Override
getVSAction()1484         String getVSAction() {
1485             return EVENT_TYPE_FADE_IN;
1486         }
1487 
FadeInEvent(@onNull AudioPlaybackConfiguration apc, boolean skipRamp, VolumeShaper.Configuration config, VolumeShaper.Operation operation)1488         FadeInEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp,
1489                 VolumeShaper.Configuration config, VolumeShaper.Operation operation) {
1490             super(apc, skipRamp, config, operation);
1491         }
1492     }
1493 
1494     private static final class AudioAttrEvent extends EventLogger.Event {
1495         private final int mPlayerIId;
1496         private final AudioAttributes mPlayerAttr;
1497 
AudioAttrEvent(int piid, AudioAttributes attr)1498         AudioAttrEvent(int piid, AudioAttributes attr) {
1499             mPlayerIId = piid;
1500             mPlayerAttr = attr;
1501         }
1502 
1503         @Override
eventToString()1504         public String eventToString() {
1505             return new String("player piid:" + mPlayerIId + " new AudioAttributes:" + mPlayerAttr);
1506         }
1507     }
1508 
1509     private static final class MuteAwaitConnectionEvent extends EventLogger.Event {
1510         private final @NonNull int[] mUsagesToMute;
1511 
MuteAwaitConnectionEvent(@onNull int[] usagesToMute)1512         MuteAwaitConnectionEvent(@NonNull int[] usagesToMute) {
1513             mUsagesToMute = usagesToMute;
1514         }
1515 
1516         @Override
eventToString()1517         public String eventToString() {
1518             return "muteAwaitConnection muting usages " + Arrays.toString(mUsagesToMute);
1519         }
1520     }
1521 
1522     private static final class PlayerFormatEvent extends EventLogger.Event {
1523         private final int mPlayerIId;
1524         private final AudioPlaybackConfiguration.FormatInfo mFormat;
1525 
PlayerFormatEvent(int piid, AudioPlaybackConfiguration.FormatInfo format)1526         PlayerFormatEvent(int piid, AudioPlaybackConfiguration.FormatInfo format) {
1527             mPlayerIId = piid;
1528             mFormat = format;
1529         }
1530 
1531         @Override
eventToString()1532         public String eventToString() {
1533             return new String("player piid:" + mPlayerIId + " format update:" + mFormat);
1534         }
1535     }
1536 
1537     static final EventLogger
1538             sEventLogger = new EventLogger(100,
1539             "playback activity as reported through PlayerBase");
1540 
1541     //==========================================================================================
1542     // Mute conditional on device connection
1543     //==========================================================================================
muteAwaitConnection(@onNull int[] usagesToMute, @NonNull AudioDeviceAttributes dev, long timeOutMs)1544     void muteAwaitConnection(@NonNull int[] usagesToMute,
1545             @NonNull AudioDeviceAttributes dev, long timeOutMs) {
1546         sEventLogger.enqueueAndLog(
1547                 "muteAwaitConnection() dev:" + dev + " timeOutMs:" + timeOutMs,
1548                 EventLogger.Event.ALOGI, TAG);
1549         synchronized (mPlayerLock) {
1550             mutePlayersExpectingDevice(usagesToMute);
1551             // schedule timeout (remove previously scheduled first)
1552             mEventHandler.removeMessages(MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION);
1553             mEventHandler.sendMessageDelayed(
1554                     mEventHandler.obtainMessage(MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION, dev),
1555                     timeOutMs);
1556         }
1557     }
1558 
cancelMuteAwaitConnection(String source)1559     void cancelMuteAwaitConnection(String source) {
1560         sEventLogger.enqueueAndLog("cancelMuteAwaitConnection() from:" + source,
1561                 EventLogger.Event.ALOGI, TAG);
1562         synchronized (mPlayerLock) {
1563             // cancel scheduled timeout, ignore device, only one expected device at a time
1564             mEventHandler.removeMessages(MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION);
1565             // unmute immediately
1566             unmutePlayersExpectingDevice();
1567         }
1568     }
1569 
1570     /**
1571      * List of the piids of the players that are muted until a specific audio device connects
1572      */
1573     @GuardedBy("mPlayerLock")
1574     private final ArrayList<Integer> mMutedPlayersAwaitingConnection = new ArrayList<Integer>();
1575 
1576     /**
1577      * List of AudioAttributes usages to mute until a specific audio device connects
1578      */
1579     @GuardedBy("mPlayerLock")
1580     private @Nullable int[] mMutedUsagesAwaitingConnection = null;
1581 
1582     @GuardedBy("mPlayerLock")
mutePlayersExpectingDevice(@onNull int[] usagesToMute)1583     private void mutePlayersExpectingDevice(@NonNull int[] usagesToMute) {
1584         sEventLogger.enqueue(new MuteAwaitConnectionEvent(usagesToMute));
1585         mMutedUsagesAwaitingConnection = usagesToMute;
1586         final Set<Integer> piidSet = mPlayers.keySet();
1587         final Iterator<Integer> piidIterator = piidSet.iterator();
1588         // find which players to mute
1589         while (piidIterator.hasNext()) {
1590             final Integer piid = piidIterator.next();
1591             final AudioPlaybackConfiguration apc = mPlayers.get(piid);
1592             if (apc == null) {
1593                 continue;
1594             }
1595             maybeMutePlayerAwaitingConnection(apc);
1596         }
1597     }
1598 
1599     @GuardedBy("mPlayerLock")
maybeMutePlayerAwaitingConnection(@onNull AudioPlaybackConfiguration apc)1600     private void maybeMutePlayerAwaitingConnection(@NonNull AudioPlaybackConfiguration apc) {
1601         if (mMutedUsagesAwaitingConnection == null) {
1602             return;
1603         }
1604         for (int usage : mMutedUsagesAwaitingConnection) {
1605             if (usage == apc.getAudioAttributes().getUsage()) {
1606                 try {
1607                     sEventLogger.enqueue((new EventLogger.StringEvent(
1608                             "awaiting connection: muting piid:"
1609                                     + apc.getPlayerInterfaceId()
1610                                     + " uid:" + apc.getClientUid())).printLog(TAG));
1611                     apc.getPlayerProxy().applyVolumeShaper(
1612                             MUTE_AWAIT_CONNECTION_VSHAPE,
1613                             PLAY_SKIP_RAMP);
1614                     mMutedPlayersAwaitingConnection.add(apc.getPlayerInterfaceId());
1615                 } catch (Exception e) {
1616                     Log.e(TAG, "awaiting connection: error muting player "
1617                             + apc.getPlayerInterfaceId(), e);
1618                 }
1619             }
1620         }
1621     }
1622 
1623     @GuardedBy("mPlayerLock")
unmutePlayersExpectingDevice()1624     private void unmutePlayersExpectingDevice() {
1625         mMutedUsagesAwaitingConnection = null;
1626         for (int piid : mMutedPlayersAwaitingConnection) {
1627             final AudioPlaybackConfiguration apc = mPlayers.get(piid);
1628             if (apc == null) {
1629                 continue;
1630             }
1631             try {
1632                 sEventLogger.enqueue(new EventLogger.StringEvent(
1633                         "unmuting piid:" + piid).printLog(TAG));
1634                 apc.getPlayerProxy().applyVolumeShaper(MUTE_AWAIT_CONNECTION_VSHAPE,
1635                         VolumeShaper.Operation.REVERSE);
1636             } catch (Exception e) {
1637                 Log.e(TAG, "Error unmuting player " + piid + " uid:"
1638                         + apc.getClientUid(), e);
1639             }
1640         }
1641         mMutedPlayersAwaitingConnection.clear();
1642     }
1643 
1644     //=================================================================
1645     // Message handling
1646     private Handler mEventHandler;
1647     private HandlerThread mEventThread;
1648 
1649     /**
1650      * timeout for a mute awaiting a device connection
1651      * args:
1652      *     msg.obj: the audio device being expected
1653      *         type: AudioDeviceAttributes
1654      */
1655     private static final int MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION = 1;
1656 
1657     /**
1658      * event for player getting muted
1659      * args:
1660      *     msg.arg1: piid
1661      *     msg.arg2: port id
1662      *     msg.obj: extras describing the mute reason
1663      *         type: PersistableBundle
1664      */
1665     private static final int MSG_IIL_UPDATE_PLAYER_MUTED_EVENT = 2;
1666 
1667     /**
1668      * event for player reporting playback format and spatialization status
1669      * args:
1670      *     msg.arg1: piid
1671      *     msg.arg2: port id
1672      *     msg.obj: extras describing the sample rate, channel mask, spatialized
1673      *         type: PersistableBundle
1674      */
1675     private static final int MSG_IIL_UPDATE_PLAYER_FORMAT = 3;
1676 
initEventHandler()1677     private void initEventHandler() {
1678         mEventThread = new HandlerThread(TAG);
1679         mEventThread.start();
1680         mEventHandler = new Handler(mEventThread.getLooper()) {
1681             @Override
1682             public void handleMessage(Message msg) {
1683                 switch (msg.what) {
1684                     case MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION:
1685                         sEventLogger.enqueueAndLog("Timeout for muting waiting for "
1686                                 + (AudioDeviceAttributes) msg.obj + ", unmuting",
1687                                 EventLogger.Event.ALOGI, TAG);
1688                         synchronized (mPlayerLock) {
1689                             unmutePlayersExpectingDevice();
1690                         }
1691                         mMuteAwaitConnectionTimeoutCb.accept((AudioDeviceAttributes) msg.obj);
1692                         break;
1693 
1694                     case MSG_IIL_UPDATE_PLAYER_MUTED_EVENT:
1695                         // TODO: replace PersistableBundle with own struct
1696                         PersistableBundle extras = (PersistableBundle) msg.obj;
1697                         if (extras == null) {
1698                             Log.w(TAG, "Received mute event with no extras");
1699                             break;
1700                         }
1701                         @PlayerMuteEvent int eventValue = extras.getInt(EXTRA_PLAYER_EVENT_MUTE);
1702 
1703                         synchronized (mPlayerLock) {
1704                             int piid = msg.arg1;
1705 
1706                             sEventLogger.enqueue(
1707                                     new PlayerEvent(piid, PLAYER_UPDATE_MUTED, eventValue));
1708 
1709                             final AudioPlaybackConfiguration apc = mPlayers.get(piid);
1710                             if (apc == null || !apc.handleMutedEvent(eventValue)) {
1711                                 break;  // do not dispatch
1712                             }
1713                             dispatchPlaybackChange(/* iplayerReleased= */false);
1714                         }
1715                         break;
1716 
1717                     case MSG_IIL_UPDATE_PLAYER_FORMAT:
1718                         final PersistableBundle formatExtras = (PersistableBundle) msg.obj;
1719                         if (formatExtras == null) {
1720                             Log.w(TAG, "Received format event with no extras");
1721                             break;
1722                         }
1723                         final boolean spatialized = formatExtras.getBoolean(
1724                                 AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_SPATIALIZED, false);
1725                         final int sampleRate = formatExtras.getInt(
1726                                 AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_SAMPLE_RATE, 0);
1727                         final int nativeChannelMask = formatExtras.getInt(
1728                                 AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_CHANNEL_MASK, 0);
1729                         final FormatInfo format =
1730                                 new FormatInfo(spatialized, nativeChannelMask, sampleRate);
1731 
1732                         sEventLogger.enqueue(new PlayerFormatEvent(msg.arg1, format));
1733 
1734                         final AudioPlaybackConfiguration apc;
1735                         synchronized (mPlayerLock) {
1736                             apc = mPlayers.get(msg.arg1);
1737                         }
1738                         if (apc == null || !apc.handleFormatEvent(format)) {
1739                             break;  // do not dispatch
1740                         }
1741                         // TODO optimize for no dispatch to non-privileged listeners
1742                         dispatchPlaybackChange(/* iplayerReleased= */false);
1743                         break;
1744                     default:
1745                         break;
1746                 }
1747             }
1748         };
1749     }
1750 }
1751