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.annotation.Nullable; 21 import android.content.Context; 22 import android.content.pm.PackageManager; 23 import android.media.AudioAttributes; 24 import android.media.AudioDeviceAttributes; 25 import android.media.AudioManager; 26 import android.media.AudioPlaybackConfiguration; 27 import android.media.AudioSystem; 28 import android.media.IPlaybackConfigDispatcher; 29 import android.media.PlayerBase; 30 import android.media.VolumeShaper; 31 import android.os.Binder; 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.os.IBinder; 35 import android.os.Message; 36 import android.os.RemoteException; 37 import android.util.Log; 38 39 import com.android.internal.annotations.GuardedBy; 40 import com.android.internal.util.ArrayUtils; 41 42 import java.io.PrintWriter; 43 import java.text.DateFormat; 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.Collections; 47 import java.util.Date; 48 import java.util.HashMap; 49 import java.util.Iterator; 50 import java.util.List; 51 import java.util.Set; 52 import java.util.concurrent.ConcurrentLinkedQueue; 53 import java.util.function.Consumer; 54 55 /** 56 * Class to receive and dispatch updates from AudioSystem about recording configurations. 57 */ 58 public final class PlaybackActivityMonitor 59 implements AudioPlaybackConfiguration.PlayerDeathMonitor, PlayerFocusEnforcer { 60 61 public static final String TAG = "AS.PlayActivityMonitor"; 62 63 /*package*/ static final boolean DEBUG = false; 64 /*package*/ static final int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1; 65 /*package*/ static final int VOLUME_SHAPER_SYSTEM_FADEOUT_ID = 2; 66 /*package*/ static final int VOLUME_SHAPER_SYSTEM_MUTE_AWAIT_CONNECTION_ID = 3; 67 /*package*/ static final int VOLUME_SHAPER_SYSTEM_STRONG_DUCK_ID = 4; 68 69 // ducking settings for a "normal duck" at -14dB 70 private static final VolumeShaper.Configuration DUCK_VSHAPE = 71 new VolumeShaper.Configuration.Builder() 72 .setId(VOLUME_SHAPER_SYSTEM_DUCK_ID) 73 .setCurve(new float[] { 0.f, 1.f } /* times */, 74 new float[] { 1.f, 0.2f } /* volumes */) 75 .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME) 76 .setDuration(MediaFocusControl.getFocusRampTimeMs( 77 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 78 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION) 79 .build())) 80 .build(); 81 private static final VolumeShaper.Configuration DUCK_ID = 82 new VolumeShaper.Configuration(VOLUME_SHAPER_SYSTEM_DUCK_ID); 83 84 // ducking settings for a "strong duck" at -35dB (attenuation factor of 0.017783) 85 private static final VolumeShaper.Configuration STRONG_DUCK_VSHAPE = 86 new VolumeShaper.Configuration.Builder() 87 .setId(VOLUME_SHAPER_SYSTEM_STRONG_DUCK_ID) 88 .setCurve(new float[] { 0.f, 1.f } /* times */, 89 new float[] { 1.f, 0.017783f } /* volumes */) 90 .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME) 91 .setDuration(MediaFocusControl.getFocusRampTimeMs( 92 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 93 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION) 94 .build())) 95 .build(); 96 private static final VolumeShaper.Configuration STRONG_DUCK_ID = 97 new VolumeShaper.Configuration(VOLUME_SHAPER_SYSTEM_STRONG_DUCK_ID); 98 99 private static final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED = 100 new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY) 101 .createIfNeeded() 102 .build(); 103 104 private static final long UNMUTE_DURATION_MS = 100; 105 private static final VolumeShaper.Configuration MUTE_AWAIT_CONNECTION_VSHAPE = 106 new VolumeShaper.Configuration.Builder() 107 .setId(VOLUME_SHAPER_SYSTEM_MUTE_AWAIT_CONNECTION_ID) 108 .setCurve(new float[] { 0.f, 1.f } /* times */, 109 new float[] { 1.f, 0.f } /* volumes */) 110 .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME) 111 // even though we specify a duration, it's only used for the unmute, 112 // for muting this volume shaper is run with PLAY_SKIP_RAMP 113 .setDuration(UNMUTE_DURATION_MS) 114 .build(); 115 116 // TODO support VolumeShaper on those players 117 private static final int[] UNDUCKABLE_PLAYER_TYPES = { 118 AudioPlaybackConfiguration.PLAYER_TYPE_AAUDIO, 119 AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL, 120 }; 121 122 // like a PLAY_CREATE_IF_NEEDED operation but with a skip to the end of the ramp 123 private static final VolumeShaper.Operation PLAY_SKIP_RAMP = 124 new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build(); 125 126 private final ConcurrentLinkedQueue<PlayMonitorClient> mClients = new ConcurrentLinkedQueue<>(); 127 128 private final Object mPlayerLock = new Object(); 129 @GuardedBy("mPlayerLock") 130 private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers = 131 new HashMap<Integer, AudioPlaybackConfiguration>(); 132 133 private final Context mContext; 134 private int mSavedAlarmVolume = -1; 135 private final int mMaxAlarmVolume; 136 private int mPrivilegedAlarmActiveCount = 0; 137 private final Consumer<AudioDeviceAttributes> mMuteAwaitConnectionTimeoutCb; 138 PlaybackActivityMonitor(Context context, int maxAlarmVolume, Consumer<AudioDeviceAttributes> muteTimeoutCallback)139 PlaybackActivityMonitor(Context context, int maxAlarmVolume, 140 Consumer<AudioDeviceAttributes> muteTimeoutCallback) { 141 mContext = context; 142 mMaxAlarmVolume = maxAlarmVolume; 143 PlayMonitorClient.sListenerDeathMonitor = this; 144 AudioPlaybackConfiguration.sPlayerDeathMonitor = this; 145 mMuteAwaitConnectionTimeoutCb = muteTimeoutCallback; 146 initEventHandler(); 147 } 148 149 //================================================================= 150 private final ArrayList<Integer> mBannedUids = new ArrayList<Integer>(); 151 152 // see AudioManagerInternal.disableAudioForUid(boolean disable, int uid) disableAudioForUid(boolean disable, int uid)153 public void disableAudioForUid(boolean disable, int uid) { 154 synchronized(mPlayerLock) { 155 final int index = mBannedUids.indexOf(new Integer(uid)); 156 if (index >= 0) { 157 if (!disable) { 158 if (DEBUG) { // hidden behind DEBUG, too noisy otherwise 159 sEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid)); 160 } 161 mBannedUids.remove(index); 162 // nothing else to do, future playback requests from this uid are ok 163 } // no else to handle, uid already present, so disabling again is no-op 164 } else { 165 if (disable) { 166 for (AudioPlaybackConfiguration apc : mPlayers.values()) { 167 checkBanPlayer(apc, uid); 168 } 169 if (DEBUG) { // hidden behind DEBUG, too noisy otherwise 170 sEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid)); 171 } 172 mBannedUids.add(new Integer(uid)); 173 } // no else to handle, uid already not in list, so enabling again is no-op 174 } 175 } 176 } 177 checkBanPlayer(@onNull AudioPlaybackConfiguration apc, int uid)178 private boolean checkBanPlayer(@NonNull AudioPlaybackConfiguration apc, int uid) { 179 final boolean toBan = (apc.getClientUid() == uid); 180 if (toBan) { 181 final int piid = apc.getPlayerInterfaceId(); 182 try { 183 Log.v(TAG, "banning player " + piid + " uid:" + uid); 184 apc.getPlayerProxy().pause(); 185 } catch (Exception e) { 186 Log.e(TAG, "error banning player " + piid + " uid:" + uid, e); 187 } 188 } 189 return toBan; 190 } 191 192 //================================================================= 193 // Player to ignore (only handling single player, designed for ignoring 194 // in the logs one specific player such as the touch sounds player) 195 @GuardedBy("mPlayerLock") 196 private ArrayList<Integer> mDoNotLogPiidList = new ArrayList<>(); 197 ignorePlayerIId(int doNotLogPiid)198 /*package*/ void ignorePlayerIId(int doNotLogPiid) { 199 synchronized (mPlayerLock) { 200 mDoNotLogPiidList.add(doNotLogPiid); 201 } 202 } 203 204 //================================================================= 205 // Track players and their states 206 // methods playerAttributes, playerEvent, releasePlayer are all oneway calls 207 // into AudioService. They trigger synchronous dispatchPlaybackChange() which updates 208 // all listeners as oneway calls. 209 trackPlayer(PlayerBase.PlayerIdCard pic)210 public int trackPlayer(PlayerBase.PlayerIdCard pic) { 211 final int newPiid = AudioSystem.newAudioPlayerId(); 212 if (DEBUG) { Log.v(TAG, "trackPlayer() new piid=" + newPiid); } 213 final AudioPlaybackConfiguration apc = 214 new AudioPlaybackConfiguration(pic, newPiid, 215 Binder.getCallingUid(), Binder.getCallingPid()); 216 apc.init(); 217 synchronized (mAllowedCapturePolicies) { 218 int uid = apc.getClientUid(); 219 if (mAllowedCapturePolicies.containsKey(uid)) { 220 updateAllowedCapturePolicy(apc, mAllowedCapturePolicies.get(uid)); 221 } 222 } 223 sEventLogger.log(new NewPlayerEvent(apc)); 224 synchronized(mPlayerLock) { 225 mPlayers.put(newPiid, apc); 226 maybeMutePlayerAwaitingConnection(apc); 227 } 228 return newPiid; 229 } 230 playerAttributes(int piid, @NonNull AudioAttributes attr, int binderUid)231 public void playerAttributes(int piid, @NonNull AudioAttributes attr, int binderUid) { 232 final boolean change; 233 synchronized (mAllowedCapturePolicies) { 234 if (mAllowedCapturePolicies.containsKey(binderUid) 235 && attr.getAllowedCapturePolicy() < mAllowedCapturePolicies.get(binderUid)) { 236 attr = new AudioAttributes.Builder(attr) 237 .setAllowedCapturePolicy(mAllowedCapturePolicies.get(binderUid)).build(); 238 } 239 } 240 synchronized(mPlayerLock) { 241 final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); 242 if (checkConfigurationCaller(piid, apc, binderUid)) { 243 sEventLogger.log(new AudioAttrEvent(piid, attr)); 244 change = apc.handleAudioAttributesEvent(attr); 245 } else { 246 Log.e(TAG, "Error updating audio attributes"); 247 change = false; 248 } 249 } 250 if (change) { 251 dispatchPlaybackChange(false); 252 } 253 } 254 255 /** 256 * Update player session ID 257 * @param piid Player id to update 258 * @param sessionId The new audio session ID 259 * @param binderUid Calling binder uid 260 */ playerSessionId(int piid, int sessionId, int binderUid)261 public void playerSessionId(int piid, int sessionId, int binderUid) { 262 final boolean change; 263 synchronized (mPlayerLock) { 264 final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); 265 if (checkConfigurationCaller(piid, apc, binderUid)) { 266 change = apc.handleSessionIdEvent(sessionId); 267 } else { 268 Log.e(TAG, "Error updating audio session"); 269 change = false; 270 } 271 } 272 if (change) { 273 dispatchPlaybackChange(false); 274 } 275 } 276 277 private static final int FLAGS_FOR_SILENCE_OVERRIDE = 278 AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY | 279 AudioAttributes.FLAG_BYPASS_MUTE; 280 checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event)281 private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) { 282 if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID) { 283 return; 284 } 285 if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED || 286 apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { 287 if ((apc.getAudioAttributes().getAllFlags() & FLAGS_FOR_SILENCE_OVERRIDE) 288 == FLAGS_FOR_SILENCE_OVERRIDE && 289 apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_ALARM && 290 mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, 291 apc.getClientPid(), apc.getClientUid()) == 292 PackageManager.PERMISSION_GRANTED) { 293 if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED && 294 apc.getPlayerState() != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { 295 if (mPrivilegedAlarmActiveCount++ == 0) { 296 mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex( 297 AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER); 298 AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM, 299 mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); 300 } 301 } else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED && 302 apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { 303 if (--mPrivilegedAlarmActiveCount == 0) { 304 if (AudioSystem.getStreamVolumeIndex( 305 AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) == 306 mMaxAlarmVolume) { 307 AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM, 308 mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); 309 } 310 } 311 } 312 } 313 } 314 } 315 316 /** 317 * Update player event 318 * @param piid Player id to update 319 * @param event The new player event 320 * @param deviceId The new player device id 321 * @param binderUid Calling binder uid 322 */ playerEvent(int piid, int event, int deviceId, int binderUid)323 public void playerEvent(int piid, int event, int deviceId, int binderUid) { 324 if (DEBUG) { 325 Log.v(TAG, String.format("playerEvent(piid=%d, deviceId=%d, event=%s)", 326 piid, deviceId, AudioPlaybackConfiguration.playerStateToString(event))); 327 } 328 boolean change; 329 synchronized(mPlayerLock) { 330 final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); 331 if (apc == null) { 332 return; 333 } 334 335 final boolean doNotLog = mDoNotLogPiidList.contains(piid); 336 if (doNotLog && event != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) { 337 // do not log nor dispatch events for "ignored" players other than the release 338 return; 339 } 340 sEventLogger.log(new PlayerEvent(piid, event, deviceId)); 341 342 if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { 343 for (Integer uidInteger: mBannedUids) { 344 if (checkBanPlayer(apc, uidInteger.intValue())) { 345 // player was banned, do not update its state 346 sEventLogger.log(new AudioEventLogger.StringEvent( 347 "not starting piid:" + piid + " ,is banned")); 348 return; 349 } 350 } 351 } 352 if (apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL 353 && event != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) { 354 // FIXME SoundPool not ready for state reporting 355 return; 356 } 357 if (checkConfigurationCaller(piid, apc, binderUid)) { 358 //TODO add generation counter to only update to the latest state 359 checkVolumeForPrivilegedAlarm(apc, event); 360 change = apc.handleStateEvent(event, deviceId); 361 } else { 362 Log.e(TAG, "Error handling event " + event); 363 change = false; 364 } 365 if (change) { 366 if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { 367 mDuckingManager.checkDuck(apc); 368 mFadingManager.checkFade(apc); 369 } 370 if (doNotLog) { 371 // do not dispatch events for "ignored" players 372 change = false; 373 } 374 } 375 } 376 if (change) { 377 dispatchPlaybackChange(event == AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); 378 } 379 } 380 playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid)381 public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid) { 382 // no check on UID yet because this is only for logging at the moment 383 sEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid)); 384 } 385 releasePlayer(int piid, int binderUid)386 public void releasePlayer(int piid, int binderUid) { 387 if (DEBUG) { Log.v(TAG, "releasePlayer() for piid=" + piid); } 388 boolean change = false; 389 synchronized(mPlayerLock) { 390 final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); 391 if (checkConfigurationCaller(piid, apc, binderUid)) { 392 sEventLogger.log(new AudioEventLogger.StringEvent( 393 "releasing player piid:" + piid)); 394 mPlayers.remove(new Integer(piid)); 395 mDuckingManager.removeReleased(apc); 396 mFadingManager.removeReleased(apc); 397 mMutedPlayersAwaitingConnection.remove(Integer.valueOf(piid)); 398 checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); 399 change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED, 400 AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID); 401 402 if (change && mDoNotLogPiidList.contains(piid)) { 403 // do not dispatch a change for a "do not log" player 404 change = false; 405 } 406 } 407 } 408 if (change) { 409 dispatchPlaybackChange(true /*iplayerreleased*/); 410 } 411 } 412 413 /** 414 * A map of uid to capture policy. 415 */ 416 private final HashMap<Integer, Integer> mAllowedCapturePolicies = 417 new HashMap<Integer, Integer>(); 418 419 /** 420 * Cache allowed capture policy, which specifies whether the audio played by the app may or may 421 * not be captured by other apps or the system. 422 * 423 * @param uid the uid of requested app 424 * @param capturePolicy one of 425 * {@link AudioAttributes#ALLOW_CAPTURE_BY_ALL}, 426 * {@link AudioAttributes#ALLOW_CAPTURE_BY_SYSTEM}, 427 * {@link AudioAttributes#ALLOW_CAPTURE_BY_NONE}. 428 */ setAllowedCapturePolicy(int uid, int capturePolicy)429 public void setAllowedCapturePolicy(int uid, int capturePolicy) { 430 synchronized (mAllowedCapturePolicies) { 431 if (capturePolicy == AudioAttributes.ALLOW_CAPTURE_BY_ALL) { 432 // When the capture policy is ALLOW_CAPTURE_BY_ALL, it is okay to 433 // remove it from cached capture policy as it is the default value. 434 mAllowedCapturePolicies.remove(uid); 435 return; 436 } else { 437 mAllowedCapturePolicies.put(uid, capturePolicy); 438 } 439 } 440 synchronized (mPlayerLock) { 441 for (AudioPlaybackConfiguration apc : mPlayers.values()) { 442 if (apc.getClientUid() == uid) { 443 updateAllowedCapturePolicy(apc, capturePolicy); 444 } 445 } 446 } 447 } 448 449 /** 450 * Return the capture policy for given uid. 451 * @param uid the uid to query its cached capture policy. 452 * @return cached capture policy for given uid or AudioAttributes.ALLOW_CAPTURE_BY_ALL 453 * if there is not cached capture policy. 454 */ getAllowedCapturePolicy(int uid)455 public int getAllowedCapturePolicy(int uid) { 456 return mAllowedCapturePolicies.getOrDefault(uid, AudioAttributes.ALLOW_CAPTURE_BY_ALL); 457 } 458 459 /** 460 * Return a copy of all cached capture policies. 461 */ getAllAllowedCapturePolicies()462 public HashMap<Integer, Integer> getAllAllowedCapturePolicies() { 463 synchronized (mAllowedCapturePolicies) { 464 return (HashMap<Integer, Integer>) mAllowedCapturePolicies.clone(); 465 } 466 } 467 updateAllowedCapturePolicy(AudioPlaybackConfiguration apc, int capturePolicy)468 private void updateAllowedCapturePolicy(AudioPlaybackConfiguration apc, int capturePolicy) { 469 AudioAttributes attr = apc.getAudioAttributes(); 470 if (attr.getAllowedCapturePolicy() >= capturePolicy) { 471 return; 472 } 473 apc.handleAudioAttributesEvent( 474 new AudioAttributes.Builder(apc.getAudioAttributes()) 475 .setAllowedCapturePolicy(capturePolicy).build()); 476 } 477 478 // Implementation of AudioPlaybackConfiguration.PlayerDeathMonitor 479 @Override playerDeath(int piid)480 public void playerDeath(int piid) { 481 releasePlayer(piid, 0); 482 } 483 484 /** 485 * Returns true if a player belonging to the app with given uid is active. 486 * 487 * @param uid the app uid 488 * @return true if a player is active, false otherwise 489 */ isPlaybackActiveForUid(int uid)490 public boolean isPlaybackActiveForUid(int uid) { 491 synchronized (mPlayerLock) { 492 for (AudioPlaybackConfiguration apc : mPlayers.values()) { 493 if (apc.isActive() && apc.getClientUid() == uid) { 494 return true; 495 } 496 } 497 } 498 return false; 499 } 500 dump(PrintWriter pw)501 protected void dump(PrintWriter pw) { 502 // players 503 pw.println("\nPlaybackActivityMonitor dump time: " 504 + DateFormat.getTimeInstance().format(new Date())); 505 synchronized(mPlayerLock) { 506 pw.println("\n playback listeners:"); 507 for (PlayMonitorClient pmc : mClients) { 508 pw.print(" " + (pmc.isPrivileged() ? "(S)" : "(P)") 509 + pmc.toString()); 510 } 511 pw.println("\n"); 512 // all players 513 pw.println("\n players:"); 514 final List<Integer> piidIntList = new ArrayList<Integer>(mPlayers.keySet()); 515 Collections.sort(piidIntList); 516 for (Integer piidInt : piidIntList) { 517 final AudioPlaybackConfiguration apc = mPlayers.get(piidInt); 518 if (apc != null) { 519 if (mDoNotLogPiidList.contains(apc.getPlayerInterfaceId())) { 520 pw.print("(not logged)"); 521 } 522 apc.dump(pw); 523 } 524 } 525 // ducked players 526 pw.println("\n ducked players piids:"); 527 mDuckingManager.dump(pw); 528 // faded out players 529 pw.println("\n faded out players piids:"); 530 mFadingManager.dump(pw); 531 // players muted due to the device ringing or being in a call 532 pw.print("\n muted player piids due to call/ring:"); 533 for (int piid : mMutedPlayers) { 534 pw.print(" " + piid); 535 } 536 pw.println(); 537 // banned players: 538 pw.print("\n banned uids:"); 539 for (int uid : mBannedUids) { 540 pw.print(" " + uid); 541 } 542 pw.println("\n"); 543 // muted players: 544 pw.print("\n muted players (piids) awaiting device connection:"); 545 for (int piid : mMutedPlayersAwaitingConnection) { 546 pw.print(" " + piid); 547 } 548 pw.println("\n"); 549 // log 550 sEventLogger.dump(pw); 551 } 552 synchronized (mAllowedCapturePolicies) { 553 pw.println("\n allowed capture policies:"); 554 for (HashMap.Entry<Integer, Integer> entry : mAllowedCapturePolicies.entrySet()) { 555 pw.println(" uid: " + entry.getKey() + " policy: " + entry.getValue()); 556 } 557 } 558 } 559 560 /** 561 * Check that piid and uid are valid for the given valid configuration. 562 * @param piid the piid of the player. 563 * @param apc the configuration found for this piid. 564 * @param binderUid actual uid of client trying to signal a player state/event/attributes. 565 * @return true if the call is valid and the change should proceed, false otherwise. Always 566 * returns false when apc is null. 567 */ checkConfigurationCaller(int piid, final AudioPlaybackConfiguration apc, int binderUid)568 private static boolean checkConfigurationCaller(int piid, 569 final AudioPlaybackConfiguration apc, int binderUid) { 570 if (apc == null) { 571 return false; 572 } else if ((binderUid != 0) && (apc.getClientUid() != binderUid)) { 573 Log.e(TAG, "Forbidden operation from uid " + binderUid + " for player " + piid); 574 return false; 575 } 576 return true; 577 } 578 579 /** 580 * Sends new list after update of playback configurations 581 * @param iplayerReleased indicates if the change was due to a player being released 582 */ dispatchPlaybackChange(boolean iplayerReleased)583 private void dispatchPlaybackChange(boolean iplayerReleased) { 584 if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); } 585 final List<AudioPlaybackConfiguration> configsSystem; 586 // list of playback configurations for "public consumption". It is computed lazy if there 587 // are non-system playback activity listeners. 588 List<AudioPlaybackConfiguration> configsPublic = null; 589 synchronized (mPlayerLock) { 590 if (mPlayers.isEmpty()) { 591 return; 592 } 593 configsSystem = new ArrayList<>(mPlayers.values()); 594 } 595 596 final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); 597 while (clientIterator.hasNext()) { 598 final PlayMonitorClient pmc = clientIterator.next(); 599 // do not spam the logs if there are problems communicating with this client 600 if (!pmc.reachedMaxErrorCount()) { 601 if (pmc.isPrivileged()) { 602 pmc.dispatchPlaybackConfigChange(configsSystem, 603 iplayerReleased); 604 } else { 605 if (configsPublic == null) { 606 configsPublic = anonymizeForPublicConsumption(configsSystem); 607 } 608 // non-system clients don't have the control interface IPlayer, so 609 // they don't need to flush commands when a player was released 610 pmc.dispatchPlaybackConfigChange(configsPublic, false); 611 } 612 } 613 } 614 } 615 anonymizeForPublicConsumption( List<AudioPlaybackConfiguration> sysConfigs)616 private ArrayList<AudioPlaybackConfiguration> anonymizeForPublicConsumption( 617 List<AudioPlaybackConfiguration> sysConfigs) { 618 ArrayList<AudioPlaybackConfiguration> publicConfigs = 619 new ArrayList<AudioPlaybackConfiguration>(); 620 // only add active anonymized configurations, 621 for (AudioPlaybackConfiguration config : sysConfigs) { 622 if (config.isActive()) { 623 publicConfigs.add(AudioPlaybackConfiguration.anonymizedCopy(config)); 624 } 625 } 626 return publicConfigs; 627 } 628 629 630 //================================================================= 631 // PlayerFocusEnforcer implementation 632 private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>(); 633 634 private final DuckingManager mDuckingManager = new DuckingManager(); 635 636 @Override duckPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser, boolean forceDuck)637 public boolean duckPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser, 638 boolean forceDuck) { 639 if (DEBUG) { 640 Log.v(TAG, String.format("duckPlayers: uids winner=%d loser=%d", 641 winner.getClientUid(), loser.getClientUid())); 642 } 643 synchronized (mPlayerLock) { 644 if (mPlayers.isEmpty()) { 645 return true; 646 } 647 // check if this UID needs to be ducked (return false if not), and gather list of 648 // eligible players to duck 649 final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator(); 650 final ArrayList<AudioPlaybackConfiguration> apcsToDuck = 651 new ArrayList<AudioPlaybackConfiguration>(); 652 while (apcIterator.hasNext()) { 653 final AudioPlaybackConfiguration apc = apcIterator.next(); 654 if (!winner.hasSameUid(apc.getClientUid()) 655 && loser.hasSameUid(apc.getClientUid()) 656 && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) 657 { 658 if (!forceDuck && (apc.getAudioAttributes().getContentType() == 659 AudioAttributes.CONTENT_TYPE_SPEECH)) { 660 // the player is speaking, ducking will make the speech unintelligible 661 // so let the app handle it instead 662 Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId() 663 + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid() 664 + " - SPEECH"); 665 return false; 666 } else if (ArrayUtils.contains(UNDUCKABLE_PLAYER_TYPES, apc.getPlayerType())) { 667 Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId() 668 + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid() 669 + " due to type:" 670 + AudioPlaybackConfiguration.toLogFriendlyPlayerType( 671 apc.getPlayerType())); 672 return false; 673 } 674 apcsToDuck.add(apc); 675 } 676 } 677 // add the players eligible for ducking to the list, and duck them 678 // (if apcsToDuck is empty, this will at least mark this uid as ducked, so when 679 // players of the same uid start, they will be ducked by DuckingManager.checkDuck()) 680 mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck, reqCausesStrongDuck(winner)); 681 } 682 return true; 683 } 684 reqCausesStrongDuck(FocusRequester requester)685 private boolean reqCausesStrongDuck(FocusRequester requester) { 686 if (requester.getGainRequest() != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) { 687 return false; 688 } 689 final int reqUsage = requester.getAudioAttributes().getUsage(); 690 if ((reqUsage == AudioAttributes.USAGE_ASSISTANT) 691 || (reqUsage == AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)) { 692 return true; 693 } 694 return false; 695 } 696 697 @Override restoreVShapedPlayers(@onNull FocusRequester winner)698 public void restoreVShapedPlayers(@NonNull FocusRequester winner) { 699 if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); } 700 synchronized (mPlayerLock) { 701 mDuckingManager.unduckUid(winner.getClientUid(), mPlayers); 702 mFadingManager.unfadeOutUid(winner.getClientUid(), mPlayers); 703 } 704 } 705 706 @Override mutePlayersForCall(int[] usagesToMute)707 public void mutePlayersForCall(int[] usagesToMute) { 708 if (DEBUG) { 709 String log = new String("mutePlayersForCall: usages="); 710 for (int usage : usagesToMute) { log += " " + usage; } 711 Log.v(TAG, log); 712 } 713 synchronized (mPlayerLock) { 714 final Set<Integer> piidSet = mPlayers.keySet(); 715 final Iterator<Integer> piidIterator = piidSet.iterator(); 716 // find which players to mute 717 while (piidIterator.hasNext()) { 718 final Integer piid = piidIterator.next(); 719 final AudioPlaybackConfiguration apc = mPlayers.get(piid); 720 if (apc == null) { 721 continue; 722 } 723 final int playerUsage = apc.getAudioAttributes().getUsage(); 724 boolean mute = false; 725 for (int usageToMute : usagesToMute) { 726 if (playerUsage == usageToMute) { 727 mute = true; 728 break; 729 } 730 } 731 if (mute) { 732 try { 733 sEventLogger.log((new AudioEventLogger.StringEvent("call: muting piid:" 734 + piid + " uid:" + apc.getClientUid())).printLog(TAG)); 735 apc.getPlayerProxy().setVolume(0.0f); 736 mMutedPlayers.add(new Integer(piid)); 737 } catch (Exception e) { 738 Log.e(TAG, "call: error muting player " + piid, e); 739 } 740 } 741 } 742 } 743 } 744 745 @Override unmutePlayersForCall()746 public void unmutePlayersForCall() { 747 if (DEBUG) { 748 Log.v(TAG, "unmutePlayersForCall()"); 749 } 750 synchronized (mPlayerLock) { 751 if (mMutedPlayers.isEmpty()) { 752 return; 753 } 754 for (int piid : mMutedPlayers) { 755 final AudioPlaybackConfiguration apc = mPlayers.get(piid); 756 if (apc != null) { 757 try { 758 sEventLogger.log(new AudioEventLogger.StringEvent("call: unmuting piid:" 759 + piid).printLog(TAG)); 760 apc.getPlayerProxy().setVolume(1.0f); 761 } catch (Exception e) { 762 Log.e(TAG, "call: error unmuting player " + piid + " uid:" 763 + apc.getClientUid(), e); 764 } 765 } 766 } 767 mMutedPlayers.clear(); 768 } 769 } 770 771 private final FadeOutManager mFadingManager = new FadeOutManager(); 772 773 /** 774 * 775 * @param winner the new non-transient focus owner 776 * @param loser the previous focus owner 777 * @return true if there are players being faded out 778 */ 779 @Override fadeOutPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser)780 public boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser) { 781 if (DEBUG) { 782 Log.v(TAG, "fadeOutPlayers: winner=" + winner.getPackageName() 783 + " loser=" + loser.getPackageName()); 784 } 785 boolean loserHasActivePlayers = false; 786 787 // find which players to fade out 788 synchronized (mPlayerLock) { 789 if (mPlayers.isEmpty()) { 790 if (DEBUG) { Log.v(TAG, "no players to fade out"); } 791 return false; 792 } 793 if (!FadeOutManager.canCauseFadeOut(winner, loser)) { 794 return false; 795 } 796 // check if this UID needs to be faded out (return false if not), and gather list of 797 // eligible players to fade out 798 final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator(); 799 final ArrayList<AudioPlaybackConfiguration> apcsToFadeOut = 800 new ArrayList<AudioPlaybackConfiguration>(); 801 while (apcIterator.hasNext()) { 802 final AudioPlaybackConfiguration apc = apcIterator.next(); 803 if (!winner.hasSameUid(apc.getClientUid()) 804 && loser.hasSameUid(apc.getClientUid()) 805 && apc.getPlayerState() 806 == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { 807 if (!FadeOutManager.canBeFadedOut(apc)) { 808 // the player is not eligible to be faded out, bail 809 Log.v(TAG, "not fading out player " + apc.getPlayerInterfaceId() 810 + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid() 811 + " type:" 812 + AudioPlaybackConfiguration.toLogFriendlyPlayerType( 813 apc.getPlayerType()) 814 + " attr:" + apc.getAudioAttributes()); 815 return false; 816 } 817 loserHasActivePlayers = true; 818 apcsToFadeOut.add(apc); 819 } 820 } 821 if (loserHasActivePlayers) { 822 mFadingManager.fadeOutUid(loser.getClientUid(), apcsToFadeOut); 823 } 824 } 825 826 return loserHasActivePlayers; 827 } 828 829 @Override forgetUid(int uid)830 public void forgetUid(int uid) { 831 final HashMap<Integer, AudioPlaybackConfiguration> players; 832 synchronized (mPlayerLock) { 833 players = (HashMap<Integer, AudioPlaybackConfiguration>) mPlayers.clone(); 834 } 835 mFadingManager.unfadeOutUid(uid, players); 836 } 837 838 //================================================================= 839 // Track playback activity listeners 840 registerPlaybackCallback(IPlaybackConfigDispatcher pcdb, boolean isPrivileged)841 void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) { 842 if (pcdb == null) { 843 return; 844 } 845 final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged); 846 if (pmc.init()) { 847 mClients.add(pmc); 848 } 849 } 850 unregisterPlaybackCallback(IPlaybackConfigDispatcher pcdb)851 void unregisterPlaybackCallback(IPlaybackConfigDispatcher pcdb) { 852 if (pcdb == null) { 853 return; 854 } 855 final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); 856 // iterate over the clients to remove the dispatcher 857 while (clientIterator.hasNext()) { 858 PlayMonitorClient pmc = clientIterator.next(); 859 if (pmc.equalsDispatcher(pcdb)) { 860 pmc.release(); 861 clientIterator.remove(); 862 } 863 } 864 } 865 getActivePlaybackConfigurations(boolean isPrivileged)866 List<AudioPlaybackConfiguration> getActivePlaybackConfigurations(boolean isPrivileged) { 867 synchronized(mPlayers) { 868 if (isPrivileged) { 869 return new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()); 870 } else { 871 final List<AudioPlaybackConfiguration> configsPublic; 872 synchronized (mPlayerLock) { 873 configsPublic = anonymizeForPublicConsumption( 874 new ArrayList<AudioPlaybackConfiguration>(mPlayers.values())); 875 } 876 return configsPublic; 877 } 878 } 879 } 880 881 882 /** 883 * Inner class to track clients that want to be notified of playback updates 884 */ 885 private static final class PlayMonitorClient implements IBinder.DeathRecipient { 886 887 // can afford to be static because only one PlaybackActivityMonitor ever instantiated 888 static PlaybackActivityMonitor sListenerDeathMonitor; 889 890 // number of errors after which we don't update this client anymore to not spam the logs 891 private static final int MAX_ERRORS = 5; 892 893 private final IPlaybackConfigDispatcher mDispatcherCb; 894 895 @GuardedBy("this") 896 private final boolean mIsPrivileged; 897 @GuardedBy("this") 898 private boolean mIsReleased = false; 899 @GuardedBy("this") 900 private int mErrorCount = 0; 901 PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged)902 PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) { 903 mDispatcherCb = pcdb; 904 mIsPrivileged = isPrivileged; 905 } 906 907 @Override binderDied()908 public void binderDied() { 909 Log.w(TAG, "client died"); 910 sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb); 911 } 912 init()913 synchronized boolean init() { 914 if (mIsReleased) { 915 // Do not init after release 916 return false; 917 } 918 try { 919 mDispatcherCb.asBinder().linkToDeath(this, 0); 920 return true; 921 } catch (RemoteException e) { 922 Log.w(TAG, "Could not link to client death", e); 923 return false; 924 } 925 } 926 release()927 synchronized void release() { 928 mDispatcherCb.asBinder().unlinkToDeath(this, 0); 929 mIsReleased = true; 930 } 931 dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs, boolean flush)932 void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs, 933 boolean flush) { 934 synchronized (this) { 935 if (mIsReleased) { 936 // Do not dispatch anything after release 937 return; 938 } 939 } 940 try { 941 mDispatcherCb.dispatchPlaybackConfigChange(configs, flush); 942 } catch (RemoteException e) { 943 synchronized (this) { 944 mErrorCount++; 945 Log.e(TAG, "Error (" + mErrorCount 946 + ") trying to dispatch playback config change to " + this, e); 947 } 948 } 949 } 950 isPrivileged()951 synchronized boolean isPrivileged() { 952 return mIsPrivileged; 953 } 954 reachedMaxErrorCount()955 synchronized boolean reachedMaxErrorCount() { 956 return mErrorCount >= MAX_ERRORS; 957 } 958 equalsDispatcher(IPlaybackConfigDispatcher pcdb)959 synchronized boolean equalsDispatcher(IPlaybackConfigDispatcher pcdb) { 960 if (pcdb == null) { 961 return false; 962 } 963 return pcdb.asBinder().equals(mDispatcherCb.asBinder()); 964 } 965 } 966 967 //================================================================= 968 // Class to handle ducking related operations for a given UID 969 private static final class DuckingManager { 970 private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>(); 971 duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck, boolean requestCausesStrongDuck)972 synchronized void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck, 973 boolean requestCausesStrongDuck) { 974 if (DEBUG) { Log.v(TAG, "DuckingManager: duckUid() uid:"+ uid); } 975 if (!mDuckers.containsKey(uid)) { 976 mDuckers.put(uid, new DuckedApp(uid, requestCausesStrongDuck)); 977 } 978 final DuckedApp da = mDuckers.get(uid); 979 for (AudioPlaybackConfiguration apc : apcsToDuck) { 980 da.addDuck(apc, false /*skipRamp*/); 981 } 982 } 983 unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players)984 synchronized void unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) { 985 if (DEBUG) { Log.v(TAG, "DuckingManager: unduckUid() uid:"+ uid); } 986 final DuckedApp da = mDuckers.remove(uid); 987 if (da == null) { 988 return; 989 } 990 da.removeUnduckAll(players); 991 } 992 993 // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED checkDuck(@onNull AudioPlaybackConfiguration apc)994 synchronized void checkDuck(@NonNull AudioPlaybackConfiguration apc) { 995 if (DEBUG) { Log.v(TAG, "DuckingManager: checkDuck() player piid:" 996 + apc.getPlayerInterfaceId()+ " uid:"+ apc.getClientUid()); } 997 final DuckedApp da = mDuckers.get(apc.getClientUid()); 998 if (da == null) { 999 return; 1000 } 1001 da.addDuck(apc, true /*skipRamp*/); 1002 } 1003 dump(PrintWriter pw)1004 synchronized void dump(PrintWriter pw) { 1005 for (DuckedApp da : mDuckers.values()) { 1006 da.dump(pw); 1007 } 1008 } 1009 removeReleased(@onNull AudioPlaybackConfiguration apc)1010 synchronized void removeReleased(@NonNull AudioPlaybackConfiguration apc) { 1011 final int uid = apc.getClientUid(); 1012 if (DEBUG) { Log.v(TAG, "DuckingManager: removedReleased() player piid: " 1013 + apc.getPlayerInterfaceId() + " uid:" + uid); } 1014 final DuckedApp da = mDuckers.get(uid); 1015 if (da == null) { 1016 return; 1017 } 1018 da.removeReleased(apc); 1019 } 1020 1021 private static final class DuckedApp { 1022 private final int mUid; 1023 /** determines whether ducking is done with DUCK_VSHAPE or STRONG_DUCK_VSHAPE */ 1024 private final boolean mUseStrongDuck; 1025 private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>(); 1026 DuckedApp(int uid, boolean useStrongDuck)1027 DuckedApp(int uid, boolean useStrongDuck) { 1028 mUid = uid; 1029 mUseStrongDuck = useStrongDuck; 1030 } 1031 dump(PrintWriter pw)1032 void dump(PrintWriter pw) { 1033 pw.print("\t uid:" + mUid + " piids:"); 1034 for (int piid : mDuckedPlayers) { 1035 pw.print(" " + piid); 1036 } 1037 pw.println(""); 1038 } 1039 1040 // pre-conditions: 1041 // * apc != null 1042 // * apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED addDuck(@onNull AudioPlaybackConfiguration apc, boolean skipRamp)1043 void addDuck(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) { 1044 final int piid = new Integer(apc.getPlayerInterfaceId()); 1045 if (mDuckedPlayers.contains(piid)) { 1046 if (DEBUG) { Log.v(TAG, "player piid:" + piid + " already ducked"); } 1047 return; 1048 } 1049 try { 1050 sEventLogger.log((new DuckEvent(apc, skipRamp, mUseStrongDuck)).printLog(TAG)); 1051 apc.getPlayerProxy().applyVolumeShaper( 1052 mUseStrongDuck ? STRONG_DUCK_VSHAPE : DUCK_VSHAPE, 1053 skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED); 1054 mDuckedPlayers.add(piid); 1055 } catch (Exception e) { 1056 Log.e(TAG, "Error ducking player piid:" + piid + " uid:" + mUid, e); 1057 } 1058 } 1059 removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players)1060 void removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players) { 1061 for (int piid : mDuckedPlayers) { 1062 final AudioPlaybackConfiguration apc = players.get(piid); 1063 if (apc != null) { 1064 try { 1065 sEventLogger.log((new AudioEventLogger.StringEvent("unducking piid:" 1066 + piid)).printLog(TAG)); 1067 apc.getPlayerProxy().applyVolumeShaper( 1068 mUseStrongDuck ? STRONG_DUCK_ID : DUCK_ID, 1069 VolumeShaper.Operation.REVERSE); 1070 } catch (Exception e) { 1071 Log.e(TAG, "Error unducking player piid:" + piid + " uid:" + mUid, e); 1072 } 1073 } else { 1074 // this piid was in the list of ducked players, but wasn't found 1075 if (DEBUG) { 1076 Log.v(TAG, "Error unducking player piid:" + piid 1077 + ", player not found for uid " + mUid); 1078 } 1079 } 1080 } 1081 mDuckedPlayers.clear(); 1082 } 1083 removeReleased(@onNull AudioPlaybackConfiguration apc)1084 void removeReleased(@NonNull AudioPlaybackConfiguration apc) { 1085 mDuckedPlayers.remove(new Integer(apc.getPlayerInterfaceId())); 1086 } 1087 } 1088 } 1089 1090 //================================================================= 1091 // For logging 1092 private final static class PlayerEvent extends AudioEventLogger.Event { 1093 // only keeping the player interface ID as it uniquely identifies the player in the event 1094 final int mPlayerIId; 1095 final int mState; 1096 final int mDeviceId; 1097 PlayerEvent(int piid, int state, int deviceId)1098 PlayerEvent(int piid, int state, int deviceId) { 1099 mPlayerIId = piid; 1100 mState = state; 1101 mDeviceId = deviceId; 1102 } 1103 1104 @Override eventToString()1105 public String eventToString() { 1106 return new StringBuilder("player piid:").append(mPlayerIId).append(" state:") 1107 .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mState)) 1108 .append(" DeviceId:").append(mDeviceId).toString(); 1109 } 1110 } 1111 1112 private final static class PlayerOpPlayAudioEvent extends AudioEventLogger.Event { 1113 // only keeping the player interface ID as it uniquely identifies the player in the event 1114 final int mPlayerIId; 1115 final boolean mHasOp; 1116 final int mUid; 1117 PlayerOpPlayAudioEvent(int piid, boolean hasOp, int uid)1118 PlayerOpPlayAudioEvent(int piid, boolean hasOp, int uid) { 1119 mPlayerIId = piid; 1120 mHasOp = hasOp; 1121 mUid = uid; 1122 } 1123 1124 @Override eventToString()1125 public String eventToString() { 1126 return new StringBuilder("player piid:").append(mPlayerIId) 1127 .append(" has OP_PLAY_AUDIO:").append(mHasOp) 1128 .append(" in uid:").append(mUid).toString(); 1129 } 1130 } 1131 1132 private final static class NewPlayerEvent extends AudioEventLogger.Event { 1133 private final int mPlayerIId; 1134 private final int mPlayerType; 1135 private final int mClientUid; 1136 private final int mClientPid; 1137 private final AudioAttributes mPlayerAttr; 1138 private final int mSessionId; 1139 NewPlayerEvent(AudioPlaybackConfiguration apc)1140 NewPlayerEvent(AudioPlaybackConfiguration apc) { 1141 mPlayerIId = apc.getPlayerInterfaceId(); 1142 mPlayerType = apc.getPlayerType(); 1143 mClientUid = apc.getClientUid(); 1144 mClientPid = apc.getClientPid(); 1145 mPlayerAttr = apc.getAudioAttributes(); 1146 mSessionId = apc.getSessionId(); 1147 } 1148 1149 @Override eventToString()1150 public String eventToString() { 1151 return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/" 1152 + mClientPid + " type:" 1153 + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType) 1154 + " attr:" + mPlayerAttr 1155 + " session:" + mSessionId); 1156 } 1157 } 1158 1159 private abstract static class VolumeShaperEvent extends AudioEventLogger.Event { 1160 private final int mPlayerIId; 1161 private final boolean mSkipRamp; 1162 private final int mClientUid; 1163 private final int mClientPid; 1164 getVSAction()1165 abstract String getVSAction(); 1166 VolumeShaperEvent(@onNull AudioPlaybackConfiguration apc, boolean skipRamp)1167 VolumeShaperEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) { 1168 mPlayerIId = apc.getPlayerInterfaceId(); 1169 mSkipRamp = skipRamp; 1170 mClientUid = apc.getClientUid(); 1171 mClientPid = apc.getClientPid(); 1172 } 1173 1174 @Override eventToString()1175 public String eventToString() { 1176 return new StringBuilder(getVSAction()).append(" player piid:").append(mPlayerIId) 1177 .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid) 1178 .append(" skip ramp:").append(mSkipRamp).toString(); 1179 } 1180 } 1181 1182 static final class DuckEvent extends VolumeShaperEvent { 1183 final boolean mUseStrongDuck; 1184 1185 @Override getVSAction()1186 String getVSAction() { 1187 return mUseStrongDuck ? "ducking (strong)" : "ducking"; 1188 } 1189 DuckEvent(@onNull AudioPlaybackConfiguration apc, boolean skipRamp, boolean useStrongDuck)1190 DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp, boolean useStrongDuck) 1191 { 1192 super(apc, skipRamp); 1193 mUseStrongDuck = useStrongDuck; 1194 } 1195 } 1196 1197 static final class FadeOutEvent extends VolumeShaperEvent { 1198 @Override getVSAction()1199 String getVSAction() { 1200 return "fading out"; 1201 } 1202 FadeOutEvent(@onNull AudioPlaybackConfiguration apc, boolean skipRamp)1203 FadeOutEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) { 1204 super(apc, skipRamp); 1205 } 1206 } 1207 1208 private static final class AudioAttrEvent extends AudioEventLogger.Event { 1209 private final int mPlayerIId; 1210 private final AudioAttributes mPlayerAttr; 1211 AudioAttrEvent(int piid, AudioAttributes attr)1212 AudioAttrEvent(int piid, AudioAttributes attr) { 1213 mPlayerIId = piid; 1214 mPlayerAttr = attr; 1215 } 1216 1217 @Override eventToString()1218 public String eventToString() { 1219 return new String("player piid:" + mPlayerIId + " new AudioAttributes:" + mPlayerAttr); 1220 } 1221 } 1222 1223 private static final class MuteAwaitConnectionEvent extends AudioEventLogger.Event { 1224 private final @NonNull int[] mUsagesToMute; 1225 MuteAwaitConnectionEvent(@onNull int[] usagesToMute)1226 MuteAwaitConnectionEvent(@NonNull int[] usagesToMute) { 1227 mUsagesToMute = usagesToMute; 1228 } 1229 1230 @Override eventToString()1231 public String eventToString() { 1232 return "muteAwaitConnection muting usages " + Arrays.toString(mUsagesToMute); 1233 } 1234 } 1235 1236 static final AudioEventLogger sEventLogger = new AudioEventLogger(100, 1237 "playback activity as reported through PlayerBase"); 1238 1239 //========================================================================================== 1240 // Mute conditional on device connection 1241 //========================================================================================== muteAwaitConnection(@onNull int[] usagesToMute, @NonNull AudioDeviceAttributes dev, long timeOutMs)1242 void muteAwaitConnection(@NonNull int[] usagesToMute, 1243 @NonNull AudioDeviceAttributes dev, long timeOutMs) { 1244 sEventLogger.loglogi( 1245 "muteAwaitConnection() dev:" + dev + " timeOutMs:" + timeOutMs, TAG); 1246 synchronized (mPlayerLock) { 1247 mutePlayersExpectingDevice(usagesToMute); 1248 // schedule timeout (remove previously scheduled first) 1249 mEventHandler.removeMessages(MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION); 1250 mEventHandler.sendMessageDelayed( 1251 mEventHandler.obtainMessage(MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION, dev), 1252 timeOutMs); 1253 } 1254 } 1255 cancelMuteAwaitConnection(String source)1256 void cancelMuteAwaitConnection(String source) { 1257 sEventLogger.loglogi("cancelMuteAwaitConnection() from:" + source, TAG); 1258 synchronized (mPlayerLock) { 1259 // cancel scheduled timeout, ignore device, only one expected device at a time 1260 mEventHandler.removeMessages(MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION); 1261 // unmute immediately 1262 unmutePlayersExpectingDevice(); 1263 } 1264 } 1265 1266 /** 1267 * List of the piids of the players that are muted until a specific audio device connects 1268 */ 1269 @GuardedBy("mPlayerLock") 1270 private final ArrayList<Integer> mMutedPlayersAwaitingConnection = new ArrayList<Integer>(); 1271 1272 /** 1273 * List of AudioAttributes usages to mute until a specific audio device connects 1274 */ 1275 @GuardedBy("mPlayerLock") 1276 private @Nullable int[] mMutedUsagesAwaitingConnection = null; 1277 1278 @GuardedBy("mPlayerLock") mutePlayersExpectingDevice(@onNull int[] usagesToMute)1279 private void mutePlayersExpectingDevice(@NonNull int[] usagesToMute) { 1280 sEventLogger.log(new MuteAwaitConnectionEvent(usagesToMute)); 1281 mMutedUsagesAwaitingConnection = usagesToMute; 1282 final Set<Integer> piidSet = mPlayers.keySet(); 1283 final Iterator<Integer> piidIterator = piidSet.iterator(); 1284 // find which players to mute 1285 while (piidIterator.hasNext()) { 1286 final Integer piid = piidIterator.next(); 1287 final AudioPlaybackConfiguration apc = mPlayers.get(piid); 1288 if (apc == null) { 1289 continue; 1290 } 1291 maybeMutePlayerAwaitingConnection(apc); 1292 } 1293 } 1294 1295 @GuardedBy("mPlayerLock") maybeMutePlayerAwaitingConnection(@onNull AudioPlaybackConfiguration apc)1296 private void maybeMutePlayerAwaitingConnection(@NonNull AudioPlaybackConfiguration apc) { 1297 if (mMutedUsagesAwaitingConnection == null) { 1298 return; 1299 } 1300 for (int usage : mMutedUsagesAwaitingConnection) { 1301 if (usage == apc.getAudioAttributes().getUsage()) { 1302 try { 1303 sEventLogger.log((new AudioEventLogger.StringEvent( 1304 "awaiting connection: muting piid:" 1305 + apc.getPlayerInterfaceId() 1306 + " uid:" + apc.getClientUid())).printLog(TAG)); 1307 apc.getPlayerProxy().applyVolumeShaper( 1308 MUTE_AWAIT_CONNECTION_VSHAPE, 1309 PLAY_SKIP_RAMP); 1310 mMutedPlayersAwaitingConnection.add(apc.getPlayerInterfaceId()); 1311 } catch (Exception e) { 1312 Log.e(TAG, "awaiting connection: error muting player " 1313 + apc.getPlayerInterfaceId(), e); 1314 } 1315 } 1316 } 1317 } 1318 1319 @GuardedBy("mPlayerLock") unmutePlayersExpectingDevice()1320 private void unmutePlayersExpectingDevice() { 1321 mMutedUsagesAwaitingConnection = null; 1322 for (int piid : mMutedPlayersAwaitingConnection) { 1323 final AudioPlaybackConfiguration apc = mPlayers.get(piid); 1324 if (apc == null) { 1325 continue; 1326 } 1327 try { 1328 sEventLogger.log(new AudioEventLogger.StringEvent( 1329 "unmuting piid:" + piid).printLog(TAG)); 1330 apc.getPlayerProxy().applyVolumeShaper(MUTE_AWAIT_CONNECTION_VSHAPE, 1331 VolumeShaper.Operation.REVERSE); 1332 } catch (Exception e) { 1333 Log.e(TAG, "Error unmuting player " + piid + " uid:" 1334 + apc.getClientUid(), e); 1335 } 1336 } 1337 mMutedPlayersAwaitingConnection.clear(); 1338 } 1339 1340 //================================================================= 1341 // Message handling 1342 private Handler mEventHandler; 1343 private HandlerThread mEventThread; 1344 1345 /** 1346 * timeout for a mute awaiting a device connection 1347 * args: 1348 * msg.obj: the audio device being expected 1349 * type: AudioDeviceAttributes 1350 */ 1351 private static final int MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION = 1; 1352 initEventHandler()1353 private void initEventHandler() { 1354 mEventThread = new HandlerThread(TAG); 1355 mEventThread.start(); 1356 mEventHandler = new Handler(mEventThread.getLooper()) { 1357 @Override 1358 public void handleMessage(Message msg) { 1359 switch (msg.what) { 1360 case MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION: 1361 sEventLogger.loglogi("Timeout for muting waiting for " 1362 + (AudioDeviceAttributes) msg.obj + ", unmuting", TAG); 1363 synchronized (mPlayerLock) { 1364 unmutePlayersExpectingDevice(); 1365 } 1366 mMuteAwaitConnectionTimeoutCb.accept((AudioDeviceAttributes) msg.obj); 1367 break; 1368 default: 1369 break; 1370 } 1371 } 1372 }; 1373 } 1374 } 1375