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 android.annotation.NonNull;
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.media.AudioAttributes;
23 import android.media.AudioManager;
24 import android.media.AudioPlaybackConfiguration;
25 import android.media.AudioSystem;
26 import android.media.IPlaybackConfigDispatcher;
27 import android.media.PlayerBase;
28 import android.media.VolumeShaper;
29 import android.os.Binder;
30 import android.os.IBinder;
31 import android.os.RemoteException;
32 import android.util.Log;
33 
34 import com.android.internal.util.ArrayUtils;
35 
36 import java.io.PrintWriter;
37 import java.text.DateFormat;
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.Date;
41 import java.util.HashMap;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Set;
45 
46 /**
47  * Class to receive and dispatch updates from AudioSystem about recording configurations.
48  */
49 public final class PlaybackActivityMonitor
50         implements AudioPlaybackConfiguration.PlayerDeathMonitor, PlayerFocusEnforcer {
51 
52     public static final String TAG = "AudioService.PlaybackActivityMonitor";
53 
54     private static final boolean DEBUG = false;
55     private static final int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;
56 
57     private static final VolumeShaper.Configuration DUCK_VSHAPE =
58             new VolumeShaper.Configuration.Builder()
59                 .setId(VOLUME_SHAPER_SYSTEM_DUCK_ID)
60                 .setCurve(new float[] { 0.f, 1.f } /* times */,
61                     new float[] { 1.f, 0.2f } /* volumes */)
62                 .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
63                 .setDuration(MediaFocusControl.getFocusRampTimeMs(
64                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
65                     new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION)
66                             .build()))
67                 .build();
68     private static final VolumeShaper.Configuration DUCK_ID =
69             new VolumeShaper.Configuration(VOLUME_SHAPER_SYSTEM_DUCK_ID);
70     private static final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED =
71             new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY)
72                     .createIfNeeded()
73                     .build();
74 
75     // TODO support VolumeShaper on those players
76     private static final int[] UNDUCKABLE_PLAYER_TYPES = {
77             AudioPlaybackConfiguration.PLAYER_TYPE_AAUDIO,
78             AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL,
79     };
80 
81     // like a PLAY_CREATE_IF_NEEDED operation but with a skip to the end of the ramp
82     private static final VolumeShaper.Operation PLAY_SKIP_RAMP =
83             new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build();
84 
85     private final ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>();
86     // a public client is one that needs an anonymized version of the playback configurations, we
87     // keep track of whether there is at least one to know when we need to create the list of
88     // playback configurations that do not contain uid/pid/package name information.
89     private boolean mHasPublicClients = false;
90 
91     private final Object mPlayerLock = new Object();
92     private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers =
93             new HashMap<Integer, AudioPlaybackConfiguration>();
94 
95     private final Context mContext;
96     private int mSavedAlarmVolume = -1;
97     private final int mMaxAlarmVolume;
98     private int mPrivilegedAlarmActiveCount = 0;
99 
PlaybackActivityMonitor(Context context, int maxAlarmVolume)100     PlaybackActivityMonitor(Context context, int maxAlarmVolume) {
101         mContext = context;
102         mMaxAlarmVolume = maxAlarmVolume;
103         PlayMonitorClient.sListenerDeathMonitor = this;
104         AudioPlaybackConfiguration.sPlayerDeathMonitor = this;
105     }
106 
107     //=================================================================
108     private final ArrayList<Integer> mBannedUids = new ArrayList<Integer>();
109 
110     // see AudioManagerInternal.disableAudioForUid(boolean disable, int uid)
disableAudioForUid(boolean disable, int uid)111     public void disableAudioForUid(boolean disable, int uid) {
112         synchronized(mPlayerLock) {
113             final int index = mBannedUids.indexOf(new Integer(uid));
114             if (index >= 0) {
115                 if (!disable) {
116                     if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
117                         sEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid));
118                     }
119                     mBannedUids.remove(index);
120                     // nothing else to do, future playback requests from this uid are ok
121                 } // no else to handle, uid already present, so disabling again is no-op
122             } else {
123                 if (disable) {
124                     for (AudioPlaybackConfiguration apc : mPlayers.values()) {
125                         checkBanPlayer(apc, uid);
126                     }
127                     if (DEBUG) { // hidden behind DEBUG, too noisy otherwise
128                         sEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid));
129                     }
130                     mBannedUids.add(new Integer(uid));
131                 } // no else to handle, uid already not in list, so enabling again is no-op
132             }
133         }
134     }
135 
checkBanPlayer(@onNull AudioPlaybackConfiguration apc, int uid)136     private boolean checkBanPlayer(@NonNull AudioPlaybackConfiguration apc, int uid) {
137         final boolean toBan = (apc.getClientUid() == uid);
138         if (toBan) {
139             final int piid = apc.getPlayerInterfaceId();
140             try {
141                 Log.v(TAG, "banning player " + piid + " uid:" + uid);
142                 apc.getPlayerProxy().pause();
143             } catch (Exception e) {
144                 Log.e(TAG, "error banning player " + piid + " uid:" + uid, e);
145             }
146         }
147         return toBan;
148     }
149 
150     //=================================================================
151     // Track players and their states
152     // methods playerAttributes, playerEvent, releasePlayer are all oneway calls
153     //  into AudioService. They trigger synchronous dispatchPlaybackChange() which updates
154     //  all listeners as oneway calls.
155 
trackPlayer(PlayerBase.PlayerIdCard pic)156     public int trackPlayer(PlayerBase.PlayerIdCard pic) {
157         final int newPiid = AudioSystem.newAudioPlayerId();
158         if (DEBUG) { Log.v(TAG, "trackPlayer() new piid=" + newPiid); }
159         final AudioPlaybackConfiguration apc =
160                 new AudioPlaybackConfiguration(pic, newPiid,
161                         Binder.getCallingUid(), Binder.getCallingPid());
162         apc.init();
163         synchronized (mAllowedCapturePolicies) {
164             int uid = apc.getClientUid();
165             if (mAllowedCapturePolicies.containsKey(uid)) {
166                 updateAllowedCapturePolicy(apc, mAllowedCapturePolicies.get(uid));
167             }
168         }
169         sEventLogger.log(new NewPlayerEvent(apc));
170         synchronized(mPlayerLock) {
171             mPlayers.put(newPiid, apc);
172         }
173         return newPiid;
174     }
175 
playerAttributes(int piid, @NonNull AudioAttributes attr, int binderUid)176     public void playerAttributes(int piid, @NonNull AudioAttributes attr, int binderUid) {
177         final boolean change;
178         synchronized (mAllowedCapturePolicies) {
179             if (mAllowedCapturePolicies.containsKey(binderUid)
180                     && attr.getAllowedCapturePolicy() < mAllowedCapturePolicies.get(binderUid)) {
181                 attr = new AudioAttributes.Builder(attr)
182                         .setAllowedCapturePolicy(mAllowedCapturePolicies.get(binderUid)).build();
183             }
184         }
185         synchronized(mPlayerLock) {
186             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
187             if (checkConfigurationCaller(piid, apc, binderUid)) {
188                 sEventLogger.log(new AudioAttrEvent(piid, attr));
189                 change = apc.handleAudioAttributesEvent(attr);
190             } else {
191                 Log.e(TAG, "Error updating audio attributes");
192                 change = false;
193             }
194         }
195         if (change) {
196             dispatchPlaybackChange(false);
197         }
198     }
199 
200     private static final int FLAGS_FOR_SILENCE_OVERRIDE =
201             AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
202             AudioAttributes.FLAG_BYPASS_MUTE;
203 
checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event)204     private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) {
205         if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED ||
206                 apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
207             if ((apc.getAudioAttributes().getAllFlags() & FLAGS_FOR_SILENCE_OVERRIDE)
208                         == FLAGS_FOR_SILENCE_OVERRIDE  &&
209                     apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_ALARM &&
210                     mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
211                             apc.getClientPid(), apc.getClientUid()) ==
212                             PackageManager.PERMISSION_GRANTED) {
213                 if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED &&
214                         apc.getPlayerState() != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
215                     if (mPrivilegedAlarmActiveCount++ == 0) {
216                         mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex(
217                                 AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER);
218                         AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM,
219                                 mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
220                     }
221                 } else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED &&
222                         apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
223                     if (--mPrivilegedAlarmActiveCount == 0) {
224                         if (AudioSystem.getStreamVolumeIndex(
225                                 AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) ==
226                                 mMaxAlarmVolume) {
227                             AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM,
228                                     mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER);
229                         }
230                     }
231                 }
232             }
233         }
234     }
235 
playerEvent(int piid, int event, int binderUid)236     public void playerEvent(int piid, int event, int binderUid) {
237         if (DEBUG) { Log.v(TAG, String.format("playerEvent(piid=%d, event=%d)", piid, event)); }
238         final boolean change;
239         synchronized(mPlayerLock) {
240             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
241             if (apc == null) {
242                 return;
243             }
244             sEventLogger.log(new PlayerEvent(piid, event));
245             if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
246                 for (Integer uidInteger: mBannedUids) {
247                     if (checkBanPlayer(apc, uidInteger.intValue())) {
248                         // player was banned, do not update its state
249                         sEventLogger.log(new AudioEventLogger.StringEvent(
250                                 "not starting piid:" + piid + " ,is banned"));
251                         return;
252                     }
253                 }
254             }
255             if (apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
256                 // FIXME SoundPool not ready for state reporting
257                 return;
258             }
259             if (checkConfigurationCaller(piid, apc, binderUid)) {
260                 //TODO add generation counter to only update to the latest state
261                 checkVolumeForPrivilegedAlarm(apc, event);
262                 change = apc.handleStateEvent(event);
263             } else {
264                 Log.e(TAG, "Error handling event " + event);
265                 change = false;
266             }
267             if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
268                 mDuckingManager.checkDuck(apc);
269             }
270         }
271         if (change) {
272             dispatchPlaybackChange(event == AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
273         }
274     }
275 
playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid)276     public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid) {
277         // no check on UID yet because this is only for logging at the moment
278         sEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid));
279     }
280 
releasePlayer(int piid, int binderUid)281     public void releasePlayer(int piid, int binderUid) {
282         if (DEBUG) { Log.v(TAG, "releasePlayer() for piid=" + piid); }
283         boolean change = false;
284         synchronized(mPlayerLock) {
285             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
286             if (checkConfigurationCaller(piid, apc, binderUid)) {
287                 sEventLogger.log(new AudioEventLogger.StringEvent(
288                         "releasing player piid:" + piid));
289                 mPlayers.remove(new Integer(piid));
290                 mDuckingManager.removeReleased(apc);
291                 checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
292                 change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
293             }
294         }
295         if (change) {
296             dispatchPlaybackChange(true /*iplayerreleased*/);
297         }
298     }
299 
300     /**
301      * A map of uid to capture policy.
302      */
303     private final HashMap<Integer, Integer> mAllowedCapturePolicies =
304             new HashMap<Integer, Integer>();
305 
306     /**
307      * Cache allowed capture policy, which specifies whether the audio played by the app may or may
308      * not be captured by other apps or the system.
309      *
310      * @param uid the uid of requested app
311      * @param capturePolicy one of
312      *     {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL},
313      *     {@link AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM},
314      *     {@link AudioAttributes#ALLOW_CAPTURE_BY_NONE}.
315      */
setAllowedCapturePolicy(int uid, int capturePolicy)316     public void setAllowedCapturePolicy(int uid, int capturePolicy) {
317         synchronized (mAllowedCapturePolicies) {
318             if (capturePolicy == AudioAttributes.ALLOW_CAPTURE_BY_ALL) {
319                 // When the capture policy is ALLOW_CAPTURE_BY_ALL, it is okay to
320                 // remove it from cached capture policy as it is the default value.
321                 mAllowedCapturePolicies.remove(uid);
322                 return;
323             } else {
324                 mAllowedCapturePolicies.put(uid, capturePolicy);
325             }
326         }
327         synchronized (mPlayerLock) {
328             for (AudioPlaybackConfiguration apc : mPlayers.values()) {
329                 if (apc.getClientUid() == uid) {
330                     updateAllowedCapturePolicy(apc, capturePolicy);
331                 }
332             }
333         }
334     }
335 
336     /**
337      * Return the capture policy for given uid.
338      * @param uid the uid to query its cached capture policy.
339      * @return cached capture policy for given uid or AudioAttributes.ALLOW_CAPTURE_BY_ALL
340      *         if there is not cached capture policy.
341      */
getAllowedCapturePolicy(int uid)342     public int getAllowedCapturePolicy(int uid) {
343         return mAllowedCapturePolicies.getOrDefault(uid, AudioAttributes.ALLOW_CAPTURE_BY_ALL);
344     }
345 
346     /**
347      * Return all cached capture policies.
348      */
getAllAllowedCapturePolicies()349     public HashMap<Integer, Integer> getAllAllowedCapturePolicies() {
350         return mAllowedCapturePolicies;
351     }
352 
updateAllowedCapturePolicy(AudioPlaybackConfiguration apc, int capturePolicy)353     private void updateAllowedCapturePolicy(AudioPlaybackConfiguration apc, int capturePolicy) {
354         AudioAttributes attr = apc.getAudioAttributes();
355         if (attr.getAllowedCapturePolicy() >= capturePolicy) {
356             return;
357         }
358         apc.handleAudioAttributesEvent(
359                 new AudioAttributes.Builder(apc.getAudioAttributes())
360                         .setAllowedCapturePolicy(capturePolicy).build());
361     }
362 
363     // Implementation of AudioPlaybackConfiguration.PlayerDeathMonitor
364     @Override
playerDeath(int piid)365     public void playerDeath(int piid) {
366         releasePlayer(piid, 0);
367     }
368 
dump(PrintWriter pw)369     protected void dump(PrintWriter pw) {
370         // players
371         pw.println("\nPlaybackActivityMonitor dump time: "
372                 + DateFormat.getTimeInstance().format(new Date()));
373         synchronized(mPlayerLock) {
374             pw.println("\n  playback listeners:");
375             synchronized(mClients) {
376                 for (PlayMonitorClient pmc : mClients) {
377                     pw.print(" " + (pmc.mIsPrivileged ? "(S)" : "(P)")
378                             + pmc.toString());
379                 }
380             }
381             pw.println("\n");
382             // all players
383             pw.println("\n  players:");
384             final List<Integer> piidIntList = new ArrayList<Integer>(mPlayers.keySet());
385             Collections.sort(piidIntList);
386             for (Integer piidInt : piidIntList) {
387                 final AudioPlaybackConfiguration apc = mPlayers.get(piidInt);
388                 if (apc != null) {
389                     apc.dump(pw);
390                 }
391             }
392             // ducked players
393             pw.println("\n  ducked players piids:");
394             mDuckingManager.dump(pw);
395             // players muted due to the device ringing or being in a call
396             pw.print("\n  muted player piids:");
397             for (int piid : mMutedPlayers) {
398                 pw.print(" " + piid);
399             }
400             pw.println();
401             // banned players:
402             pw.print("\n  banned uids:");
403             for (int uid : mBannedUids) {
404                 pw.print(" " + uid);
405             }
406             pw.println("\n");
407             // log
408             sEventLogger.dump(pw);
409         }
410         synchronized (mAllowedCapturePolicies) {
411             pw.println("\n  allowed capture policies:");
412             for (HashMap.Entry<Integer, Integer> entry : mAllowedCapturePolicies.entrySet()) {
413                 pw.println("  uid: " + entry.getKey() + " policy: " + entry.getValue());
414             }
415         }
416     }
417 
418     /**
419      * Check that piid and uid are valid for the given valid configuration.
420      * @param piid the piid of the player.
421      * @param apc the configuration found for this piid.
422      * @param binderUid actual uid of client trying to signal a player state/event/attributes.
423      * @return true if the call is valid and the change should proceed, false otherwise. Always
424      *      returns false when apc is null.
425      */
checkConfigurationCaller(int piid, final AudioPlaybackConfiguration apc, int binderUid)426     private static boolean checkConfigurationCaller(int piid,
427             final AudioPlaybackConfiguration apc, int binderUid) {
428         if (apc == null) {
429             return false;
430         } else if ((binderUid != 0) && (apc.getClientUid() != binderUid)) {
431             Log.e(TAG, "Forbidden operation from uid " + binderUid + " for player " + piid);
432             return false;
433         }
434         return true;
435     }
436 
437     /**
438      * Sends new list after update of playback configurations
439      * @param iplayerReleased indicates if the change was due to a player being released
440      */
dispatchPlaybackChange(boolean iplayerReleased)441     private void dispatchPlaybackChange(boolean iplayerReleased) {
442         synchronized (mClients) {
443             // typical use case, nobody is listening, don't do any work
444             if (mClients.isEmpty()) {
445                 return;
446             }
447         }
448         if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); }
449         final List<AudioPlaybackConfiguration> configsSystem;
450         // list of playback configurations for "public consumption". It is only computed if there
451         // are non-system playback activity listeners.
452         final List<AudioPlaybackConfiguration> configsPublic;
453         synchronized (mPlayerLock) {
454             if (mPlayers.isEmpty()) {
455                 return;
456             }
457             configsSystem = new ArrayList<AudioPlaybackConfiguration>(mPlayers.values());
458         }
459         synchronized (mClients) {
460             // was done at beginning of method, but could have changed
461             if (mClients.isEmpty()) {
462                 return;
463             }
464             configsPublic = mHasPublicClients ? anonymizeForPublicConsumption(configsSystem) : null;
465             final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
466             while (clientIterator.hasNext()) {
467                 final PlayMonitorClient pmc = clientIterator.next();
468                 try {
469                     // do not spam the logs if there are problems communicating with this client
470                     if (pmc.mErrorCount < PlayMonitorClient.MAX_ERRORS) {
471                         if (pmc.mIsPrivileged) {
472                             pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsSystem,
473                                     iplayerReleased);
474                         } else {
475                             // non-system clients don't have the control interface IPlayer, so
476                             // they don't need to flush commands when a player was released
477                             pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsPublic, false);
478                         }
479                     }
480                 } catch (RemoteException e) {
481                     pmc.mErrorCount++;
482                     Log.e(TAG, "Error (" + pmc.mErrorCount +
483                             ") trying to dispatch playback config change to " + pmc, e);
484                 }
485             }
486         }
487     }
488 
anonymizeForPublicConsumption( List<AudioPlaybackConfiguration> sysConfigs)489     private ArrayList<AudioPlaybackConfiguration> anonymizeForPublicConsumption(
490             List<AudioPlaybackConfiguration> sysConfigs) {
491         ArrayList<AudioPlaybackConfiguration> publicConfigs =
492                 new ArrayList<AudioPlaybackConfiguration>();
493         // only add active anonymized configurations,
494         for (AudioPlaybackConfiguration config : sysConfigs) {
495             if (config.isActive()) {
496                 publicConfigs.add(AudioPlaybackConfiguration.anonymizedCopy(config));
497             }
498         }
499         return publicConfigs;
500     }
501 
502 
503     //=================================================================
504     // PlayerFocusEnforcer implementation
505     private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();
506 
507     private final DuckingManager mDuckingManager = new DuckingManager();
508 
509     @Override
duckPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser, boolean forceDuck)510     public boolean duckPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser,
511                                boolean forceDuck) {
512         if (DEBUG) {
513             Log.v(TAG, String.format("duckPlayers: uids winner=%d loser=%d",
514                     winner.getClientUid(), loser.getClientUid()));
515         }
516         synchronized (mPlayerLock) {
517             if (mPlayers.isEmpty()) {
518                 return true;
519             }
520             // check if this UID needs to be ducked (return false if not), and gather list of
521             // eligible players to duck
522             final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator();
523             final ArrayList<AudioPlaybackConfiguration> apcsToDuck =
524                     new ArrayList<AudioPlaybackConfiguration>();
525             while (apcIterator.hasNext()) {
526                 final AudioPlaybackConfiguration apc = apcIterator.next();
527                 if (!winner.hasSameUid(apc.getClientUid())
528                         && loser.hasSameUid(apc.getClientUid())
529                         && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED)
530                 {
531                     if (!forceDuck && (apc.getAudioAttributes().getContentType() ==
532                             AudioAttributes.CONTENT_TYPE_SPEECH)) {
533                         // the player is speaking, ducking will make the speech unintelligible
534                         // so let the app handle it instead
535                         Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
536                                 + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
537                                 + " - SPEECH");
538                         return false;
539                     } else if (ArrayUtils.contains(UNDUCKABLE_PLAYER_TYPES, apc.getPlayerType())) {
540                         Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
541                                 + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
542                                 + " due to type:"
543                                 + AudioPlaybackConfiguration.toLogFriendlyPlayerType(
544                                         apc.getPlayerType()));
545                         return false;
546                     }
547                     apcsToDuck.add(apc);
548                 }
549             }
550             // add the players eligible for ducking to the list, and duck them
551             // (if apcsToDuck is empty, this will at least mark this uid as ducked, so when
552             //  players of the same uid start, they will be ducked by DuckingManager.checkDuck())
553             mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck);
554         }
555         return true;
556     }
557 
558     @Override
unduckPlayers(@onNull FocusRequester winner)559     public void unduckPlayers(@NonNull FocusRequester winner) {
560         if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); }
561         synchronized (mPlayerLock) {
562             mDuckingManager.unduckUid(winner.getClientUid(), mPlayers);
563         }
564     }
565 
566     @Override
mutePlayersForCall(int[] usagesToMute)567     public void mutePlayersForCall(int[] usagesToMute) {
568         if (DEBUG) {
569             String log = new String("mutePlayersForCall: usages=");
570             for (int usage : usagesToMute) { log += " " + usage; }
571             Log.v(TAG, log);
572         }
573         synchronized (mPlayerLock) {
574             final Set<Integer> piidSet = mPlayers.keySet();
575             final Iterator<Integer> piidIterator = piidSet.iterator();
576             // find which players to mute
577             while (piidIterator.hasNext()) {
578                 final Integer piid = piidIterator.next();
579                 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
580                 if (apc == null) {
581                     continue;
582                 }
583                 final int playerUsage = apc.getAudioAttributes().getUsage();
584                 boolean mute = false;
585                 for (int usageToMute : usagesToMute) {
586                     if (playerUsage == usageToMute) {
587                         mute = true;
588                         break;
589                     }
590                 }
591                 if (mute) {
592                     try {
593                         sEventLogger.log((new AudioEventLogger.StringEvent("call: muting piid:"
594                                 + piid + " uid:" + apc.getClientUid())).printLog(TAG));
595                         apc.getPlayerProxy().setVolume(0.0f);
596                         mMutedPlayers.add(new Integer(piid));
597                     } catch (Exception e) {
598                         Log.e(TAG, "call: error muting player " + piid, e);
599                     }
600                 }
601             }
602         }
603     }
604 
605     @Override
unmutePlayersForCall()606     public void unmutePlayersForCall() {
607         if (DEBUG) {
608             Log.v(TAG, "unmutePlayersForCall()");
609         }
610         synchronized (mPlayerLock) {
611             if (mMutedPlayers.isEmpty()) {
612                 return;
613             }
614             for (int piid : mMutedPlayers) {
615                 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
616                 if (apc != null) {
617                     try {
618                         sEventLogger.log(new AudioEventLogger.StringEvent("call: unmuting piid:"
619                                 + piid).printLog(TAG));
620                         apc.getPlayerProxy().setVolume(1.0f);
621                     } catch (Exception e) {
622                         Log.e(TAG, "call: error unmuting player " + piid + " uid:"
623                                 + apc.getClientUid(), e);
624                     }
625                 }
626             }
627             mMutedPlayers.clear();
628         }
629     }
630 
631     //=================================================================
632     // Track playback activity listeners
633 
registerPlaybackCallback(IPlaybackConfigDispatcher pcdb, boolean isPrivileged)634     void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
635         if (pcdb == null) {
636             return;
637         }
638         synchronized(mClients) {
639             final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
640             if (pmc.init()) {
641                 if (!isPrivileged) {
642                     mHasPublicClients = true;
643                 }
644                 mClients.add(pmc);
645             }
646         }
647     }
648 
unregisterPlaybackCallback(IPlaybackConfigDispatcher pcdb)649     void unregisterPlaybackCallback(IPlaybackConfigDispatcher pcdb) {
650         if (pcdb == null) {
651             return;
652         }
653         synchronized(mClients) {
654             final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
655             boolean hasPublicClients = false;
656             // iterate over the clients to remove the dispatcher to remove, and reevaluate at
657             // the same time if we still have a public client.
658             while (clientIterator.hasNext()) {
659                 PlayMonitorClient pmc = clientIterator.next();
660                 if (pcdb.equals(pmc.mDispatcherCb)) {
661                     pmc.release();
662                     clientIterator.remove();
663                 } else {
664                     if (!pmc.mIsPrivileged) {
665                         hasPublicClients = true;
666                     }
667                 }
668             }
669             mHasPublicClients = hasPublicClients;
670         }
671     }
672 
getActivePlaybackConfigurations(boolean isPrivileged)673     List<AudioPlaybackConfiguration> getActivePlaybackConfigurations(boolean isPrivileged) {
674         synchronized(mPlayers) {
675             if (isPrivileged) {
676                 return new ArrayList<AudioPlaybackConfiguration>(mPlayers.values());
677             } else {
678                 final List<AudioPlaybackConfiguration> configsPublic;
679                 synchronized (mPlayerLock) {
680                     configsPublic = anonymizeForPublicConsumption(
681                             new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()));
682                 }
683                 return configsPublic;
684             }
685         }
686     }
687 
688 
689     /**
690      * Inner class to track clients that want to be notified of playback updates
691      */
692     private static final class PlayMonitorClient implements IBinder.DeathRecipient {
693 
694         // can afford to be static because only one PlaybackActivityMonitor ever instantiated
695         static PlaybackActivityMonitor sListenerDeathMonitor;
696 
697         final IPlaybackConfigDispatcher mDispatcherCb;
698         final boolean mIsPrivileged;
699 
700         int mErrorCount = 0;
701         // number of errors after which we don't update this client anymore to not spam the logs
702         static final int MAX_ERRORS = 5;
703 
PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged)704         PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
705             mDispatcherCb = pcdb;
706             mIsPrivileged = isPrivileged;
707         }
708 
binderDied()709         public void binderDied() {
710             Log.w(TAG, "client died");
711             sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb);
712         }
713 
init()714         boolean init() {
715             try {
716                 mDispatcherCb.asBinder().linkToDeath(this, 0);
717                 return true;
718             } catch (RemoteException e) {
719                 Log.w(TAG, "Could not link to client death", e);
720                 return false;
721             }
722         }
723 
release()724         void release() {
725             mDispatcherCb.asBinder().unlinkToDeath(this, 0);
726         }
727     }
728 
729     //=================================================================
730     // Class to handle ducking related operations for a given UID
731     private static final class DuckingManager {
732         private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>();
733 
duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck)734         synchronized void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck) {
735             if (DEBUG) {  Log.v(TAG, "DuckingManager: duckUid() uid:"+ uid); }
736             if (!mDuckers.containsKey(uid)) {
737                 mDuckers.put(uid, new DuckedApp(uid));
738             }
739             final DuckedApp da = mDuckers.get(uid);
740             for (AudioPlaybackConfiguration apc : apcsToDuck) {
741                 da.addDuck(apc, false /*skipRamp*/);
742             }
743         }
744 
unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players)745         synchronized void unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
746             if (DEBUG) {  Log.v(TAG, "DuckingManager: unduckUid() uid:"+ uid); }
747             final DuckedApp da = mDuckers.remove(uid);
748             if (da == null) {
749                 return;
750             }
751             da.removeUnduckAll(players);
752         }
753 
754         // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
checkDuck(@onNull AudioPlaybackConfiguration apc)755         synchronized void checkDuck(@NonNull AudioPlaybackConfiguration apc) {
756             if (DEBUG) {  Log.v(TAG, "DuckingManager: checkDuck() player piid:"
757                     + apc.getPlayerInterfaceId()+ " uid:"+ apc.getClientUid()); }
758             final DuckedApp da = mDuckers.get(apc.getClientUid());
759             if (da == null) {
760                 return;
761             }
762             da.addDuck(apc, true /*skipRamp*/);
763         }
764 
dump(PrintWriter pw)765         synchronized void dump(PrintWriter pw) {
766             for (DuckedApp da : mDuckers.values()) {
767                 da.dump(pw);
768             }
769         }
770 
removeReleased(@onNull AudioPlaybackConfiguration apc)771         synchronized void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
772             final int uid = apc.getClientUid();
773             if (DEBUG) {  Log.v(TAG, "DuckingManager: removedReleased() player piid: "
774                     + apc.getPlayerInterfaceId() + " uid:" + uid); }
775             final DuckedApp da = mDuckers.get(uid);
776             if (da == null) {
777                 return;
778             }
779             da.removeReleased(apc);
780         }
781 
782         private static final class DuckedApp {
783             private final int mUid;
784             private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
785 
DuckedApp(int uid)786             DuckedApp(int uid) {
787                 mUid = uid;
788             }
789 
dump(PrintWriter pw)790             void dump(PrintWriter pw) {
791                 pw.print("\t uid:" + mUid + " piids:");
792                 for (int piid : mDuckedPlayers) {
793                     pw.print(" " + piid);
794                 }
795                 pw.println("");
796             }
797 
798             // pre-conditions:
799             //  * apc != null
800             //  * apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
addDuck(@onNull AudioPlaybackConfiguration apc, boolean skipRamp)801             void addDuck(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
802                 final int piid = new Integer(apc.getPlayerInterfaceId());
803                 if (mDuckedPlayers.contains(piid)) {
804                     if (DEBUG) { Log.v(TAG, "player piid:" + piid + " already ducked"); }
805                     return;
806                 }
807                 try {
808                     sEventLogger.log((new DuckEvent(apc, skipRamp)).printLog(TAG));
809                     apc.getPlayerProxy().applyVolumeShaper(
810                             DUCK_VSHAPE,
811                             skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
812                     mDuckedPlayers.add(piid);
813                 } catch (Exception e) {
814                     Log.e(TAG, "Error ducking player piid:" + piid + " uid:" + mUid, e);
815                 }
816             }
817 
removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players)818             void removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players) {
819                 for (int piid : mDuckedPlayers) {
820                     final AudioPlaybackConfiguration apc = players.get(piid);
821                     if (apc != null) {
822                         try {
823                             sEventLogger.log((new AudioEventLogger.StringEvent("unducking piid:"
824                                     + piid)).printLog(TAG));
825                             apc.getPlayerProxy().applyVolumeShaper(
826                                     DUCK_ID,
827                                     VolumeShaper.Operation.REVERSE);
828                         } catch (Exception e) {
829                             Log.e(TAG, "Error unducking player piid:" + piid + " uid:" + mUid, e);
830                         }
831                     } else {
832                         // this piid was in the list of ducked players, but wasn't found
833                         if (DEBUG) {
834                             Log.v(TAG, "Error unducking player piid:" + piid
835                                     + ", player not found for uid " + mUid);
836                         }
837                     }
838                 }
839                 mDuckedPlayers.clear();
840             }
841 
removeReleased(@onNull AudioPlaybackConfiguration apc)842             void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
843                 mDuckedPlayers.remove(new Integer(apc.getPlayerInterfaceId()));
844             }
845         }
846     }
847 
848     //=================================================================
849     // For logging
850     private final static class PlayerEvent extends AudioEventLogger.Event {
851         // only keeping the player interface ID as it uniquely identifies the player in the event
852         final int mPlayerIId;
853         final int mState;
854 
PlayerEvent(int piid, int state)855         PlayerEvent(int piid, int state) {
856             mPlayerIId = piid;
857             mState = state;
858         }
859 
860         @Override
eventToString()861         public String eventToString() {
862             return new StringBuilder("player piid:").append(mPlayerIId).append(" state:")
863                     .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mState)).toString();
864         }
865     }
866 
867     private final static class PlayerOpPlayAudioEvent extends AudioEventLogger.Event {
868         // only keeping the player interface ID as it uniquely identifies the player in the event
869         final int mPlayerIId;
870         final boolean mHasOp;
871         final int mUid;
872 
PlayerOpPlayAudioEvent(int piid, boolean hasOp, int uid)873         PlayerOpPlayAudioEvent(int piid, boolean hasOp, int uid) {
874             mPlayerIId = piid;
875             mHasOp = hasOp;
876             mUid = uid;
877         }
878 
879         @Override
eventToString()880         public String eventToString() {
881             return new StringBuilder("player piid:").append(mPlayerIId)
882                     .append(" has OP_PLAY_AUDIO:").append(mHasOp)
883                     .append(" in uid:").append(mUid).toString();
884         }
885     }
886 
887     private final static class NewPlayerEvent extends AudioEventLogger.Event {
888         private final int mPlayerIId;
889         private final int mPlayerType;
890         private final int mClientUid;
891         private final int mClientPid;
892         private final AudioAttributes mPlayerAttr;
893 
NewPlayerEvent(AudioPlaybackConfiguration apc)894         NewPlayerEvent(AudioPlaybackConfiguration apc) {
895             mPlayerIId = apc.getPlayerInterfaceId();
896             mPlayerType = apc.getPlayerType();
897             mClientUid = apc.getClientUid();
898             mClientPid = apc.getClientPid();
899             mPlayerAttr = apc.getAudioAttributes();
900         }
901 
902         @Override
eventToString()903         public String eventToString() {
904             return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/"
905                     + mClientPid + " type:"
906                     + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
907                     + " attr:" + mPlayerAttr);
908         }
909     }
910 
911     private static final class DuckEvent extends AudioEventLogger.Event {
912         private final int mPlayerIId;
913         private final boolean mSkipRamp;
914         private final int mClientUid;
915         private final int mClientPid;
916 
DuckEvent(@onNull AudioPlaybackConfiguration apc, boolean skipRamp)917         DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
918             mPlayerIId = apc.getPlayerInterfaceId();
919             mSkipRamp = skipRamp;
920             mClientUid = apc.getClientUid();
921             mClientPid = apc.getClientPid();
922         }
923 
924         @Override
eventToString()925         public String eventToString() {
926             return new StringBuilder("ducking player piid:").append(mPlayerIId)
927                     .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid)
928                     .append(" skip ramp:").append(mSkipRamp).toString();
929         }
930     }
931 
932     private static final class AudioAttrEvent extends AudioEventLogger.Event {
933         private final int mPlayerIId;
934         private final AudioAttributes mPlayerAttr;
935 
AudioAttrEvent(int piid, AudioAttributes attr)936         AudioAttrEvent(int piid, AudioAttributes attr) {
937             mPlayerIId = piid;
938             mPlayerAttr = attr;
939         }
940 
941         @Override
eventToString()942         public String eventToString() {
943             return new String("player piid:" + mPlayerIId + " new AudioAttributes:" + mPlayerAttr);
944         }
945     }
946 
947     private static final AudioEventLogger sEventLogger = new AudioEventLogger(100,
948             "playback activity as reported through PlayerBase");
949 }
950