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