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