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