• 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 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 
369     /**
370      * Returns true if a player belonging to the app with given uid is active.
371      *
372      * @param uid the app uid
373      * @return true if a player is active, false otherwise
374      */
isPlaybackActiveForUid(int uid)375     public boolean isPlaybackActiveForUid(int uid) {
376         synchronized (mPlayerLock) {
377             for (AudioPlaybackConfiguration apc : mPlayers.values()) {
378                 if (apc.isActive() && apc.getClientUid() == uid) {
379                     return true;
380                 }
381             }
382         }
383         return false;
384     }
385 
dump(PrintWriter pw)386     protected void dump(PrintWriter pw) {
387         // players
388         pw.println("\nPlaybackActivityMonitor dump time: "
389                 + DateFormat.getTimeInstance().format(new Date()));
390         synchronized(mPlayerLock) {
391             pw.println("\n  playback listeners:");
392             synchronized(mClients) {
393                 for (PlayMonitorClient pmc : mClients) {
394                     pw.print(" " + (pmc.mIsPrivileged ? "(S)" : "(P)")
395                             + pmc.toString());
396                 }
397             }
398             pw.println("\n");
399             // all players
400             pw.println("\n  players:");
401             final List<Integer> piidIntList = new ArrayList<Integer>(mPlayers.keySet());
402             Collections.sort(piidIntList);
403             for (Integer piidInt : piidIntList) {
404                 final AudioPlaybackConfiguration apc = mPlayers.get(piidInt);
405                 if (apc != null) {
406                     apc.dump(pw);
407                 }
408             }
409             // ducked players
410             pw.println("\n  ducked players piids:");
411             mDuckingManager.dump(pw);
412             // players muted due to the device ringing or being in a call
413             pw.print("\n  muted player piids:");
414             for (int piid : mMutedPlayers) {
415                 pw.print(" " + piid);
416             }
417             pw.println();
418             // banned players:
419             pw.print("\n  banned uids:");
420             for (int uid : mBannedUids) {
421                 pw.print(" " + uid);
422             }
423             pw.println("\n");
424             // log
425             sEventLogger.dump(pw);
426         }
427         synchronized (mAllowedCapturePolicies) {
428             pw.println("\n  allowed capture policies:");
429             for (HashMap.Entry<Integer, Integer> entry : mAllowedCapturePolicies.entrySet()) {
430                 pw.println("  uid: " + entry.getKey() + " policy: " + entry.getValue());
431             }
432         }
433     }
434 
435     /**
436      * Check that piid and uid are valid for the given valid configuration.
437      * @param piid the piid of the player.
438      * @param apc the configuration found for this piid.
439      * @param binderUid actual uid of client trying to signal a player state/event/attributes.
440      * @return true if the call is valid and the change should proceed, false otherwise. Always
441      *      returns false when apc is null.
442      */
checkConfigurationCaller(int piid, final AudioPlaybackConfiguration apc, int binderUid)443     private static boolean checkConfigurationCaller(int piid,
444             final AudioPlaybackConfiguration apc, int binderUid) {
445         if (apc == null) {
446             return false;
447         } else if ((binderUid != 0) && (apc.getClientUid() != binderUid)) {
448             Log.e(TAG, "Forbidden operation from uid " + binderUid + " for player " + piid);
449             return false;
450         }
451         return true;
452     }
453 
454     /**
455      * Sends new list after update of playback configurations
456      * @param iplayerReleased indicates if the change was due to a player being released
457      */
dispatchPlaybackChange(boolean iplayerReleased)458     private void dispatchPlaybackChange(boolean iplayerReleased) {
459         synchronized (mClients) {
460             // typical use case, nobody is listening, don't do any work
461             if (mClients.isEmpty()) {
462                 return;
463             }
464         }
465         if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); }
466         final List<AudioPlaybackConfiguration> configsSystem;
467         // list of playback configurations for "public consumption". It is only computed if there
468         // are non-system playback activity listeners.
469         final List<AudioPlaybackConfiguration> configsPublic;
470         synchronized (mPlayerLock) {
471             if (mPlayers.isEmpty()) {
472                 return;
473             }
474             configsSystem = new ArrayList<AudioPlaybackConfiguration>(mPlayers.values());
475         }
476         synchronized (mClients) {
477             // was done at beginning of method, but could have changed
478             if (mClients.isEmpty()) {
479                 return;
480             }
481             configsPublic = mHasPublicClients ? anonymizeForPublicConsumption(configsSystem) : null;
482             final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
483             while (clientIterator.hasNext()) {
484                 final PlayMonitorClient pmc = clientIterator.next();
485                 try {
486                     // do not spam the logs if there are problems communicating with this client
487                     if (pmc.mErrorCount < PlayMonitorClient.MAX_ERRORS) {
488                         if (pmc.mIsPrivileged) {
489                             pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsSystem,
490                                     iplayerReleased);
491                         } else {
492                             // non-system clients don't have the control interface IPlayer, so
493                             // they don't need to flush commands when a player was released
494                             pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsPublic, false);
495                         }
496                     }
497                 } catch (RemoteException e) {
498                     pmc.mErrorCount++;
499                     Log.e(TAG, "Error (" + pmc.mErrorCount +
500                             ") trying to dispatch playback config change to " + pmc, e);
501                 }
502             }
503         }
504     }
505 
anonymizeForPublicConsumption( List<AudioPlaybackConfiguration> sysConfigs)506     private ArrayList<AudioPlaybackConfiguration> anonymizeForPublicConsumption(
507             List<AudioPlaybackConfiguration> sysConfigs) {
508         ArrayList<AudioPlaybackConfiguration> publicConfigs =
509                 new ArrayList<AudioPlaybackConfiguration>();
510         // only add active anonymized configurations,
511         for (AudioPlaybackConfiguration config : sysConfigs) {
512             if (config.isActive()) {
513                 publicConfigs.add(AudioPlaybackConfiguration.anonymizedCopy(config));
514             }
515         }
516         return publicConfigs;
517     }
518 
519 
520     //=================================================================
521     // PlayerFocusEnforcer implementation
522     private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();
523 
524     private final DuckingManager mDuckingManager = new DuckingManager();
525 
526     @Override
duckPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser, boolean forceDuck)527     public boolean duckPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser,
528                                boolean forceDuck) {
529         if (DEBUG) {
530             Log.v(TAG, String.format("duckPlayers: uids winner=%d loser=%d",
531                     winner.getClientUid(), loser.getClientUid()));
532         }
533         synchronized (mPlayerLock) {
534             if (mPlayers.isEmpty()) {
535                 return true;
536             }
537             // check if this UID needs to be ducked (return false if not), and gather list of
538             // eligible players to duck
539             final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator();
540             final ArrayList<AudioPlaybackConfiguration> apcsToDuck =
541                     new ArrayList<AudioPlaybackConfiguration>();
542             while (apcIterator.hasNext()) {
543                 final AudioPlaybackConfiguration apc = apcIterator.next();
544                 if (!winner.hasSameUid(apc.getClientUid())
545                         && loser.hasSameUid(apc.getClientUid())
546                         && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED)
547                 {
548                     if (!forceDuck && (apc.getAudioAttributes().getContentType() ==
549                             AudioAttributes.CONTENT_TYPE_SPEECH)) {
550                         // the player is speaking, ducking will make the speech unintelligible
551                         // so let the app handle it instead
552                         Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
553                                 + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
554                                 + " - SPEECH");
555                         return false;
556                     } else if (ArrayUtils.contains(UNDUCKABLE_PLAYER_TYPES, apc.getPlayerType())) {
557                         Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
558                                 + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid()
559                                 + " due to type:"
560                                 + AudioPlaybackConfiguration.toLogFriendlyPlayerType(
561                                         apc.getPlayerType()));
562                         return false;
563                     }
564                     apcsToDuck.add(apc);
565                 }
566             }
567             // add the players eligible for ducking to the list, and duck them
568             // (if apcsToDuck is empty, this will at least mark this uid as ducked, so when
569             //  players of the same uid start, they will be ducked by DuckingManager.checkDuck())
570             mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck);
571         }
572         return true;
573     }
574 
575     @Override
unduckPlayers(@onNull FocusRequester winner)576     public void unduckPlayers(@NonNull FocusRequester winner) {
577         if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); }
578         synchronized (mPlayerLock) {
579             mDuckingManager.unduckUid(winner.getClientUid(), mPlayers);
580         }
581     }
582 
583     @Override
mutePlayersForCall(int[] usagesToMute)584     public void mutePlayersForCall(int[] usagesToMute) {
585         if (DEBUG) {
586             String log = new String("mutePlayersForCall: usages=");
587             for (int usage : usagesToMute) { log += " " + usage; }
588             Log.v(TAG, log);
589         }
590         synchronized (mPlayerLock) {
591             final Set<Integer> piidSet = mPlayers.keySet();
592             final Iterator<Integer> piidIterator = piidSet.iterator();
593             // find which players to mute
594             while (piidIterator.hasNext()) {
595                 final Integer piid = piidIterator.next();
596                 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
597                 if (apc == null) {
598                     continue;
599                 }
600                 final int playerUsage = apc.getAudioAttributes().getUsage();
601                 boolean mute = false;
602                 for (int usageToMute : usagesToMute) {
603                     if (playerUsage == usageToMute) {
604                         mute = true;
605                         break;
606                     }
607                 }
608                 if (mute) {
609                     try {
610                         sEventLogger.log((new AudioEventLogger.StringEvent("call: muting piid:"
611                                 + piid + " uid:" + apc.getClientUid())).printLog(TAG));
612                         apc.getPlayerProxy().setVolume(0.0f);
613                         mMutedPlayers.add(new Integer(piid));
614                     } catch (Exception e) {
615                         Log.e(TAG, "call: error muting player " + piid, e);
616                     }
617                 }
618             }
619         }
620     }
621 
622     @Override
unmutePlayersForCall()623     public void unmutePlayersForCall() {
624         if (DEBUG) {
625             Log.v(TAG, "unmutePlayersForCall()");
626         }
627         synchronized (mPlayerLock) {
628             if (mMutedPlayers.isEmpty()) {
629                 return;
630             }
631             for (int piid : mMutedPlayers) {
632                 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
633                 if (apc != null) {
634                     try {
635                         sEventLogger.log(new AudioEventLogger.StringEvent("call: unmuting piid:"
636                                 + piid).printLog(TAG));
637                         apc.getPlayerProxy().setVolume(1.0f);
638                     } catch (Exception e) {
639                         Log.e(TAG, "call: error unmuting player " + piid + " uid:"
640                                 + apc.getClientUid(), e);
641                     }
642                 }
643             }
644             mMutedPlayers.clear();
645         }
646     }
647 
648     //=================================================================
649     // Track playback activity listeners
650 
registerPlaybackCallback(IPlaybackConfigDispatcher pcdb, boolean isPrivileged)651     void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
652         if (pcdb == null) {
653             return;
654         }
655         synchronized(mClients) {
656             final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
657             if (pmc.init()) {
658                 if (!isPrivileged) {
659                     mHasPublicClients = true;
660                 }
661                 mClients.add(pmc);
662             }
663         }
664     }
665 
unregisterPlaybackCallback(IPlaybackConfigDispatcher pcdb)666     void unregisterPlaybackCallback(IPlaybackConfigDispatcher pcdb) {
667         if (pcdb == null) {
668             return;
669         }
670         synchronized(mClients) {
671             final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
672             boolean hasPublicClients = false;
673             // iterate over the clients to remove the dispatcher to remove, and reevaluate at
674             // the same time if we still have a public client.
675             while (clientIterator.hasNext()) {
676                 PlayMonitorClient pmc = clientIterator.next();
677                 if (pcdb.equals(pmc.mDispatcherCb)) {
678                     pmc.release();
679                     clientIterator.remove();
680                 } else {
681                     if (!pmc.mIsPrivileged) {
682                         hasPublicClients = true;
683                     }
684                 }
685             }
686             mHasPublicClients = hasPublicClients;
687         }
688     }
689 
getActivePlaybackConfigurations(boolean isPrivileged)690     List<AudioPlaybackConfiguration> getActivePlaybackConfigurations(boolean isPrivileged) {
691         synchronized(mPlayers) {
692             if (isPrivileged) {
693                 return new ArrayList<AudioPlaybackConfiguration>(mPlayers.values());
694             } else {
695                 final List<AudioPlaybackConfiguration> configsPublic;
696                 synchronized (mPlayerLock) {
697                     configsPublic = anonymizeForPublicConsumption(
698                             new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()));
699                 }
700                 return configsPublic;
701             }
702         }
703     }
704 
705 
706     /**
707      * Inner class to track clients that want to be notified of playback updates
708      */
709     private static final class PlayMonitorClient implements IBinder.DeathRecipient {
710 
711         // can afford to be static because only one PlaybackActivityMonitor ever instantiated
712         static PlaybackActivityMonitor sListenerDeathMonitor;
713 
714         final IPlaybackConfigDispatcher mDispatcherCb;
715         final boolean mIsPrivileged;
716 
717         int mErrorCount = 0;
718         // number of errors after which we don't update this client anymore to not spam the logs
719         static final int MAX_ERRORS = 5;
720 
PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged)721         PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
722             mDispatcherCb = pcdb;
723             mIsPrivileged = isPrivileged;
724         }
725 
binderDied()726         public void binderDied() {
727             Log.w(TAG, "client died");
728             sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb);
729         }
730 
init()731         boolean init() {
732             try {
733                 mDispatcherCb.asBinder().linkToDeath(this, 0);
734                 return true;
735             } catch (RemoteException e) {
736                 Log.w(TAG, "Could not link to client death", e);
737                 return false;
738             }
739         }
740 
release()741         void release() {
742             mDispatcherCb.asBinder().unlinkToDeath(this, 0);
743         }
744     }
745 
746     //=================================================================
747     // Class to handle ducking related operations for a given UID
748     private static final class DuckingManager {
749         private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>();
750 
duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck)751         synchronized void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck) {
752             if (DEBUG) {  Log.v(TAG, "DuckingManager: duckUid() uid:"+ uid); }
753             if (!mDuckers.containsKey(uid)) {
754                 mDuckers.put(uid, new DuckedApp(uid));
755             }
756             final DuckedApp da = mDuckers.get(uid);
757             for (AudioPlaybackConfiguration apc : apcsToDuck) {
758                 da.addDuck(apc, false /*skipRamp*/);
759             }
760         }
761 
unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players)762         synchronized void unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) {
763             if (DEBUG) {  Log.v(TAG, "DuckingManager: unduckUid() uid:"+ uid); }
764             final DuckedApp da = mDuckers.remove(uid);
765             if (da == null) {
766                 return;
767             }
768             da.removeUnduckAll(players);
769         }
770 
771         // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
checkDuck(@onNull AudioPlaybackConfiguration apc)772         synchronized void checkDuck(@NonNull AudioPlaybackConfiguration apc) {
773             if (DEBUG) {  Log.v(TAG, "DuckingManager: checkDuck() player piid:"
774                     + apc.getPlayerInterfaceId()+ " uid:"+ apc.getClientUid()); }
775             final DuckedApp da = mDuckers.get(apc.getClientUid());
776             if (da == null) {
777                 return;
778             }
779             da.addDuck(apc, true /*skipRamp*/);
780         }
781 
dump(PrintWriter pw)782         synchronized void dump(PrintWriter pw) {
783             for (DuckedApp da : mDuckers.values()) {
784                 da.dump(pw);
785             }
786         }
787 
removeReleased(@onNull AudioPlaybackConfiguration apc)788         synchronized void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
789             final int uid = apc.getClientUid();
790             if (DEBUG) {  Log.v(TAG, "DuckingManager: removedReleased() player piid: "
791                     + apc.getPlayerInterfaceId() + " uid:" + uid); }
792             final DuckedApp da = mDuckers.get(uid);
793             if (da == null) {
794                 return;
795             }
796             da.removeReleased(apc);
797         }
798 
799         private static final class DuckedApp {
800             private final int mUid;
801             private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
802 
DuckedApp(int uid)803             DuckedApp(int uid) {
804                 mUid = uid;
805             }
806 
dump(PrintWriter pw)807             void dump(PrintWriter pw) {
808                 pw.print("\t uid:" + mUid + " piids:");
809                 for (int piid : mDuckedPlayers) {
810                     pw.print(" " + piid);
811                 }
812                 pw.println("");
813             }
814 
815             // pre-conditions:
816             //  * apc != null
817             //  * apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
addDuck(@onNull AudioPlaybackConfiguration apc, boolean skipRamp)818             void addDuck(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
819                 final int piid = new Integer(apc.getPlayerInterfaceId());
820                 if (mDuckedPlayers.contains(piid)) {
821                     if (DEBUG) { Log.v(TAG, "player piid:" + piid + " already ducked"); }
822                     return;
823                 }
824                 try {
825                     sEventLogger.log((new DuckEvent(apc, skipRamp)).printLog(TAG));
826                     apc.getPlayerProxy().applyVolumeShaper(
827                             DUCK_VSHAPE,
828                             skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED);
829                     mDuckedPlayers.add(piid);
830                 } catch (Exception e) {
831                     Log.e(TAG, "Error ducking player piid:" + piid + " uid:" + mUid, e);
832                 }
833             }
834 
removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players)835             void removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players) {
836                 for (int piid : mDuckedPlayers) {
837                     final AudioPlaybackConfiguration apc = players.get(piid);
838                     if (apc != null) {
839                         try {
840                             sEventLogger.log((new AudioEventLogger.StringEvent("unducking piid:"
841                                     + piid)).printLog(TAG));
842                             apc.getPlayerProxy().applyVolumeShaper(
843                                     DUCK_ID,
844                                     VolumeShaper.Operation.REVERSE);
845                         } catch (Exception e) {
846                             Log.e(TAG, "Error unducking player piid:" + piid + " uid:" + mUid, e);
847                         }
848                     } else {
849                         // this piid was in the list of ducked players, but wasn't found
850                         if (DEBUG) {
851                             Log.v(TAG, "Error unducking player piid:" + piid
852                                     + ", player not found for uid " + mUid);
853                         }
854                     }
855                 }
856                 mDuckedPlayers.clear();
857             }
858 
removeReleased(@onNull AudioPlaybackConfiguration apc)859             void removeReleased(@NonNull AudioPlaybackConfiguration apc) {
860                 mDuckedPlayers.remove(new Integer(apc.getPlayerInterfaceId()));
861             }
862         }
863     }
864 
865     //=================================================================
866     // For logging
867     private final static class PlayerEvent 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 int mState;
871 
PlayerEvent(int piid, int state)872         PlayerEvent(int piid, int state) {
873             mPlayerIId = piid;
874             mState = state;
875         }
876 
877         @Override
eventToString()878         public String eventToString() {
879             return new StringBuilder("player piid:").append(mPlayerIId).append(" state:")
880                     .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mState)).toString();
881         }
882     }
883 
884     private final static class PlayerOpPlayAudioEvent extends AudioEventLogger.Event {
885         // only keeping the player interface ID as it uniquely identifies the player in the event
886         final int mPlayerIId;
887         final boolean mHasOp;
888         final int mUid;
889 
PlayerOpPlayAudioEvent(int piid, boolean hasOp, int uid)890         PlayerOpPlayAudioEvent(int piid, boolean hasOp, int uid) {
891             mPlayerIId = piid;
892             mHasOp = hasOp;
893             mUid = uid;
894         }
895 
896         @Override
eventToString()897         public String eventToString() {
898             return new StringBuilder("player piid:").append(mPlayerIId)
899                     .append(" has OP_PLAY_AUDIO:").append(mHasOp)
900                     .append(" in uid:").append(mUid).toString();
901         }
902     }
903 
904     private final static class NewPlayerEvent extends AudioEventLogger.Event {
905         private final int mPlayerIId;
906         private final int mPlayerType;
907         private final int mClientUid;
908         private final int mClientPid;
909         private final AudioAttributes mPlayerAttr;
910 
NewPlayerEvent(AudioPlaybackConfiguration apc)911         NewPlayerEvent(AudioPlaybackConfiguration apc) {
912             mPlayerIId = apc.getPlayerInterfaceId();
913             mPlayerType = apc.getPlayerType();
914             mClientUid = apc.getClientUid();
915             mClientPid = apc.getClientPid();
916             mPlayerAttr = apc.getAudioAttributes();
917         }
918 
919         @Override
eventToString()920         public String eventToString() {
921             return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/"
922                     + mClientPid + " type:"
923                     + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
924                     + " attr:" + mPlayerAttr);
925         }
926     }
927 
928     private static final class DuckEvent extends AudioEventLogger.Event {
929         private final int mPlayerIId;
930         private final boolean mSkipRamp;
931         private final int mClientUid;
932         private final int mClientPid;
933 
DuckEvent(@onNull AudioPlaybackConfiguration apc, boolean skipRamp)934         DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) {
935             mPlayerIId = apc.getPlayerInterfaceId();
936             mSkipRamp = skipRamp;
937             mClientUid = apc.getClientUid();
938             mClientPid = apc.getClientPid();
939         }
940 
941         @Override
eventToString()942         public String eventToString() {
943             return new StringBuilder("ducking player piid:").append(mPlayerIId)
944                     .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid)
945                     .append(" skip ramp:").append(mSkipRamp).toString();
946         }
947     }
948 
949     private static final class AudioAttrEvent extends AudioEventLogger.Event {
950         private final int mPlayerIId;
951         private final AudioAttributes mPlayerAttr;
952 
AudioAttrEvent(int piid, AudioAttributes attr)953         AudioAttrEvent(int piid, AudioAttributes attr) {
954             mPlayerIId = piid;
955             mPlayerAttr = attr;
956         }
957 
958         @Override
eventToString()959         public String eventToString() {
960             return new String("player piid:" + mPlayerIId + " new AudioAttributes:" + mPlayerAttr);
961         }
962     }
963 
964     private static final AudioEventLogger sEventLogger = new AudioEventLogger(100,
965             "playback activity as reported through PlayerBase");
966 }
967