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