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