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