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