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 android.media; 18 19 import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL; 20 import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE; 21 22 import android.annotation.IntDef; 23 import android.annotation.IntRange; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.RequiresPermission; 27 import android.annotation.SystemApi; 28 import android.os.Binder; 29 import android.os.IBinder; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.os.RemoteException; 33 import android.util.Log; 34 35 import com.android.internal.annotations.GuardedBy; 36 37 import java.io.PrintWriter; 38 import java.lang.annotation.Retention; 39 import java.lang.annotation.RetentionPolicy; 40 import java.util.Objects; 41 42 /** 43 * The AudioPlaybackConfiguration class collects the information describing an audio playback 44 * session. 45 */ 46 public final class AudioPlaybackConfiguration implements Parcelable { 47 private static final String TAG = new String("AudioPlaybackConfiguration"); 48 49 private static final boolean DEBUG = false; 50 51 /** @hide */ 52 public static final int PLAYER_PIID_INVALID = -1; 53 /** @hide */ 54 public static final int PLAYER_UPID_INVALID = -1; 55 /** @hide */ 56 public static final int PLAYER_DEVICEID_INVALID = 0; 57 58 // information about the implementation 59 /** 60 * @hide 61 * An unknown type of player 62 */ 63 @SystemApi 64 public static final int PLAYER_TYPE_UNKNOWN = -1; 65 /** 66 * @hide 67 * Player backed by a java android.media.AudioTrack player 68 */ 69 @SystemApi 70 public static final int PLAYER_TYPE_JAM_AUDIOTRACK = 1; 71 /** 72 * @hide 73 * Player backed by a java android.media.MediaPlayer player 74 */ 75 @SystemApi 76 public static final int PLAYER_TYPE_JAM_MEDIAPLAYER = 2; 77 /** 78 * @hide 79 * Player backed by a java android.media.SoundPool player 80 */ 81 @SystemApi 82 public static final int PLAYER_TYPE_JAM_SOUNDPOOL = 3; 83 /** 84 * @hide 85 * Player backed by a C OpenSL ES AudioPlayer player with a BufferQueue source 86 */ 87 @SystemApi 88 public static final int PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11; 89 /** 90 * @hide 91 * Player backed by a C OpenSL ES AudioPlayer player with a URI or FD source 92 */ 93 @SystemApi 94 public static final int PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12; 95 96 /** 97 * @hide 98 * Player backed an AAudio player. 99 */ 100 @SystemApi 101 public static final int PLAYER_TYPE_AAUDIO = 13; 102 103 /** 104 * @hide 105 * Player backed a hardware source, whose state is visible in the Android audio policy manager. 106 * Note this type is not in System API so it will not be returned in public API calls 107 */ 108 // TODO unhide for SystemApi, update getPlayerType() 109 public static final int PLAYER_TYPE_HW_SOURCE = 14; 110 111 /** 112 * @hide 113 * Player is a proxy for an audio player whose audio and state doesn't go through the Android 114 * audio framework. 115 * Note this type is not in System API so it will not be returned in public API calls 116 */ 117 // TODO unhide for SystemApi, update getPlayerType() 118 public static final int PLAYER_TYPE_EXTERNAL_PROXY = 15; 119 120 /** @hide */ 121 @IntDef({ 122 PLAYER_TYPE_UNKNOWN, 123 PLAYER_TYPE_JAM_AUDIOTRACK, 124 PLAYER_TYPE_JAM_MEDIAPLAYER, 125 PLAYER_TYPE_JAM_SOUNDPOOL, 126 PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE, 127 PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD, 128 }) 129 @Retention(RetentionPolicy.SOURCE) 130 public @interface PlayerType {} 131 132 /** 133 * @hide 134 * An unknown player state 135 */ 136 @SystemApi 137 public static final int PLAYER_STATE_UNKNOWN = -1; 138 /** 139 * @hide 140 * The resources of the player have been released, it cannot play anymore 141 */ 142 @SystemApi 143 public static final int PLAYER_STATE_RELEASED = 0; 144 /** 145 * @hide 146 * The state of a player when it's created 147 */ 148 @SystemApi 149 public static final int PLAYER_STATE_IDLE = 1; 150 /** 151 * @hide 152 * The state of a player that is actively playing 153 */ 154 @SystemApi 155 public static final int PLAYER_STATE_STARTED = 2; 156 /** 157 * @hide 158 * The state of a player where playback is paused 159 */ 160 @SystemApi 161 public static final int PLAYER_STATE_PAUSED = 3; 162 /** 163 * @hide 164 * The state of a player where playback is stopped 165 */ 166 @SystemApi 167 public static final int PLAYER_STATE_STOPPED = 4; 168 /** 169 * @hide 170 * The state used to update device id, does not actually change the state of the player 171 */ 172 public static final int PLAYER_UPDATE_DEVICE_ID = 5; 173 /** 174 * @hide 175 * The state used to update port id, does not actually change the state of the player 176 */ 177 public static final int PLAYER_UPDATE_PORT_ID = 6; 178 /** 179 * @hide 180 * Used to update the mute state of a player through its port id 181 */ 182 public static final int PLAYER_UPDATE_MUTED = 7; 183 /** 184 * @hide 185 * Used to update the spatialization status and format of a player through its port id 186 */ 187 public static final int PLAYER_UPDATE_FORMAT = 8; 188 189 /** @hide */ 190 @IntDef({ 191 PLAYER_STATE_UNKNOWN, 192 PLAYER_STATE_RELEASED, 193 PLAYER_STATE_IDLE, 194 PLAYER_STATE_STARTED, 195 PLAYER_STATE_PAUSED, 196 PLAYER_STATE_STOPPED, 197 PLAYER_UPDATE_DEVICE_ID, 198 PLAYER_UPDATE_PORT_ID, 199 PLAYER_UPDATE_MUTED, 200 PLAYER_UPDATE_FORMAT, 201 }) 202 @Retention(RetentionPolicy.SOURCE) 203 public @interface PlayerState {} 204 205 /** @hide */ playerStateToString(@layerState int state)206 public static String playerStateToString(@PlayerState int state) { 207 switch (state) { 208 case PLAYER_STATE_UNKNOWN: return "PLAYER_STATE_UNKNOWN"; 209 case PLAYER_STATE_RELEASED: return "PLAYER_STATE_RELEASED"; 210 case PLAYER_STATE_IDLE: return "PLAYER_STATE_IDLE"; 211 case PLAYER_STATE_STARTED: return "PLAYER_STATE_STARTED"; 212 case PLAYER_STATE_PAUSED: return "PLAYER_STATE_PAUSED"; 213 case PLAYER_STATE_STOPPED: return "PLAYER_STATE_STOPPED"; 214 case PLAYER_UPDATE_DEVICE_ID: return "PLAYER_UPDATE_DEVICE_ID"; 215 case PLAYER_UPDATE_PORT_ID: return "PLAYER_UPDATE_PORT_ID"; 216 case PLAYER_UPDATE_MUTED: return "PLAYER_UPDATE_MUTED"; 217 case PLAYER_UPDATE_FORMAT: return "PLAYER_UPDATE_FORMAT"; 218 default: 219 return "invalid state " + state; 220 } 221 } 222 223 /** 224 * @hide 225 * Used to update the spatialization status of a player through its port ID. Must be kept in 226 * sync with frameworks/native/include/audiomanager/AudioManager.h 227 */ 228 public static final String EXTRA_PLAYER_EVENT_SPATIALIZED = 229 "android.media.extra.PLAYER_EVENT_SPATIALIZED"; 230 /** 231 * @hide 232 * Used to update the sample rate of a player through its port ID. Must be kept in sync with 233 * frameworks/native/include/audiomanager/AudioManager.h 234 */ 235 public static final String EXTRA_PLAYER_EVENT_SAMPLE_RATE = 236 "android.media.extra.PLAYER_EVENT_SAMPLE_RATE"; 237 /** 238 * @hide 239 * Used to update the channel mask of a player through its port ID. Must be kept in sync with 240 * frameworks/native/include/audiomanager/AudioManager.h 241 */ 242 public static final String EXTRA_PLAYER_EVENT_CHANNEL_MASK = 243 "android.media.extra.PLAYER_EVENT_CHANNEL_MASK"; 244 /** 245 * @hide 246 * Used to update the mute state of a player through its port ID. Must be kept in sync with 247 * frameworks/native/include/audiomanager/AudioManager.h 248 */ 249 public static final String EXTRA_PLAYER_EVENT_MUTE = 250 "android.media.extra.PLAYER_EVENT_MUTE"; 251 252 /** 253 * @hide 254 * Flag used when muted by master volume. 255 */ 256 @SystemApi 257 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) 258 public static final int MUTED_BY_MASTER = (1 << 0); 259 /** 260 * @hide 261 * Flag used when muted by stream volume. 262 */ 263 @SystemApi 264 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) 265 public static final int MUTED_BY_STREAM_VOLUME = (1 << 1); 266 /** 267 * @hide 268 * Flag used when muted by stream mute. 269 */ 270 @SystemApi 271 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) 272 public static final int MUTED_BY_STREAM_MUTED = (1 << 2); 273 /** 274 * @hide 275 * Flag used when playback is muted by AppOpsManager#OP_PLAY_AUDIO. 276 */ 277 @SystemApi 278 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) 279 public static final int MUTED_BY_APP_OPS = (1 << 3); 280 /** 281 * @hide 282 * Flag used when muted by client volume. 283 */ 284 @SystemApi 285 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) 286 public static final int MUTED_BY_CLIENT_VOLUME = (1 << 4); 287 /** 288 * @hide 289 * Flag used when muted by volume shaper. 290 */ 291 @SystemApi 292 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) 293 public static final int MUTED_BY_VOLUME_SHAPER = (1 << 5); 294 295 /** @hide */ 296 @IntDef( 297 flag = true, 298 value = {MUTED_BY_MASTER, MUTED_BY_STREAM_VOLUME, MUTED_BY_STREAM_MUTED, 299 MUTED_BY_APP_OPS, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER}) 300 @Retention(RetentionPolicy.SOURCE) 301 public @interface PlayerMuteEvent { 302 } 303 304 // immutable data 305 private final int mPlayerIId; 306 307 // not final due to anonymization step 308 private int mPlayerType; 309 private int mClientUid; 310 private int mClientPid; 311 // the IPlayer reference and death monitor 312 private IPlayerShell mIPlayerShell; 313 314 private int mPlayerState; 315 private AudioAttributes mPlayerAttr; // never null 316 317 // lock for updateable properties 318 private final Object mUpdateablePropLock = new Object(); 319 320 @GuardedBy("mUpdateablePropLock") 321 private int mDeviceId; 322 @GuardedBy("mUpdateablePropLock") 323 private int mSessionId; 324 @GuardedBy("mUpdateablePropLock") 325 private @NonNull FormatInfo mFormatInfo; 326 @GuardedBy("mUpdateablePropLock") 327 @PlayerMuteEvent private int mMutedState; 328 329 /** 330 * Never use without initializing parameters afterwards 331 */ AudioPlaybackConfiguration(int piid)332 private AudioPlaybackConfiguration(int piid) { 333 mPlayerIId = piid; 334 mIPlayerShell = null; 335 } 336 337 /** 338 * @hide 339 */ AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid)340 public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) { 341 if (DEBUG) { 342 Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer 343 + " sessionId=" + pic.mSessionId); 344 } 345 mPlayerIId = piid; 346 mPlayerType = pic.mPlayerType; 347 mClientUid = uid; 348 mClientPid = pid; 349 mMutedState = 0; 350 mDeviceId = PLAYER_DEVICEID_INVALID; 351 mPlayerState = PLAYER_STATE_IDLE; 352 mPlayerAttr = pic.mAttributes; 353 if ((sPlayerDeathMonitor != null) && (pic.mIPlayer != null)) { 354 mIPlayerShell = new IPlayerShell(this, pic.mIPlayer); 355 } else { 356 mIPlayerShell = null; 357 } 358 mSessionId = pic.mSessionId; 359 mFormatInfo = FormatInfo.DEFAULT; 360 } 361 362 /** 363 * @hide 364 */ init()365 public void init() { 366 synchronized (this) { 367 if (mIPlayerShell != null) { 368 mIPlayerShell.monitorDeath(); 369 } 370 } 371 } 372 373 // sets the fields that are updateable and require synchronization setUpdateableFields(int deviceId, int sessionId, int mutedState, FormatInfo format)374 private void setUpdateableFields(int deviceId, int sessionId, int mutedState, FormatInfo format) 375 { 376 synchronized (mUpdateablePropLock) { 377 mDeviceId = deviceId; 378 mSessionId = sessionId; 379 mMutedState = mutedState; 380 mFormatInfo = format; 381 } 382 } 383 // Note that this method is called server side, so no "privileged" information is ever sent 384 // to a client that is not supposed to have access to it. 385 /** 386 * @hide 387 * Creates a copy of the playback configuration that is stripped of any data enabling 388 * identification of which application it is associated with ("anonymized"). 389 * @param in the instance to copy from 390 */ anonymizedCopy(AudioPlaybackConfiguration in)391 public static AudioPlaybackConfiguration anonymizedCopy(AudioPlaybackConfiguration in) { 392 final AudioPlaybackConfiguration anonymCopy = new AudioPlaybackConfiguration(in.mPlayerIId); 393 anonymCopy.mPlayerState = in.mPlayerState; 394 // do not reuse the full attributes: only usage, content type and public flags are allowed 395 AudioAttributes.Builder builder = new AudioAttributes.Builder() 396 .setContentType(in.mPlayerAttr.getContentType()) 397 .setFlags(in.mPlayerAttr.getFlags()) 398 .setAllowedCapturePolicy( 399 in.mPlayerAttr.getAllowedCapturePolicy() == ALLOW_CAPTURE_BY_ALL 400 ? ALLOW_CAPTURE_BY_ALL : ALLOW_CAPTURE_BY_NONE); 401 if (AudioAttributes.isSystemUsage(in.mPlayerAttr.getSystemUsage())) { 402 builder.setSystemUsage(in.mPlayerAttr.getSystemUsage()); 403 } else { 404 builder.setUsage(in.mPlayerAttr.getUsage()); 405 } 406 anonymCopy.mPlayerAttr = builder.build(); 407 // anonymized data 408 anonymCopy.mPlayerType = PLAYER_TYPE_UNKNOWN; 409 anonymCopy.mClientUid = PLAYER_UPID_INVALID; 410 anonymCopy.mClientPid = PLAYER_UPID_INVALID; 411 anonymCopy.mIPlayerShell = null; 412 anonymCopy.setUpdateableFields( 413 /*deviceId*/ PLAYER_DEVICEID_INVALID, 414 /*sessionId*/ AudioSystem.AUDIO_SESSION_ALLOCATE, 415 /*mutedState*/ 0, 416 FormatInfo.DEFAULT); 417 return anonymCopy; 418 } 419 420 /** 421 * Return the {@link AudioAttributes} of the corresponding player. 422 * @return the audio attributes of the player 423 */ getAudioAttributes()424 public AudioAttributes getAudioAttributes() { 425 return mPlayerAttr; 426 } 427 428 /** 429 * @hide 430 * Return the uid of the client application that created this player. 431 * @return the uid of the client 432 */ 433 @SystemApi getClientUid()434 public int getClientUid() { 435 return mClientUid; 436 } 437 438 /** 439 * @hide 440 * Return the pid of the client application that created this player. 441 * @return the pid of the client 442 */ 443 @SystemApi getClientPid()444 public int getClientPid() { 445 return mClientPid; 446 } 447 448 /** 449 * Returns information about the {@link AudioDeviceInfo} used for this playback. 450 * @return the audio playback device or null if the device is not available at the time of query 451 */ getAudioDeviceInfo()452 public @Nullable AudioDeviceInfo getAudioDeviceInfo() { 453 final int deviceId; 454 synchronized (mUpdateablePropLock) { 455 deviceId = mDeviceId; 456 } 457 if (deviceId == PLAYER_DEVICEID_INVALID) { 458 return null; 459 } 460 return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_OUTPUTS); 461 } 462 463 /** 464 * @hide 465 * Return the audio session ID associated with this player. 466 * See {@link AudioManager#generateAudioSessionId()}. 467 * @return an audio session ID 468 */ 469 @SystemApi getSessionId()470 public @IntRange(from = 0) int getSessionId() { 471 synchronized (mUpdateablePropLock) { 472 return mSessionId; 473 } 474 } 475 476 /** 477 * @hide 478 * Used for determining if the current player is muted. 479 * <br>Note that if this result is true then {@link #getMutedBy} will be > 0. 480 * @return {@code true} if the player associated with this configuration has been muted (by any 481 * given MUTED_BY_* source event) or {@code false} otherwise. 482 */ 483 @SystemApi 484 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) isMuted()485 public boolean isMuted() { 486 synchronized (mUpdateablePropLock) { 487 return mMutedState != 0; 488 } 489 } 490 491 /** 492 * @hide 493 * Returns a bitmask expressing the mute state as a combination of MUTED_BY_* flags. 494 * <br>A value of 0 corresponds to an unmuted player. 495 * @return the mute state. 496 */ 497 @SystemApi 498 @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) getMutedBy()499 @PlayerMuteEvent public int getMutedBy() { 500 synchronized (mUpdateablePropLock) { 501 return mMutedState; 502 } 503 } 504 505 /** 506 * @hide 507 * Return the type of player linked to this configuration. 508 * <br>Note that player types not exposed in the system API will be represented as 509 * {@link #PLAYER_TYPE_UNKNOWN}. 510 * @return the type of the player. 511 */ 512 @SystemApi getPlayerType()513 public @PlayerType int getPlayerType() { 514 switch (mPlayerType) { 515 case PLAYER_TYPE_HW_SOURCE: 516 case PLAYER_TYPE_EXTERNAL_PROXY: 517 return PLAYER_TYPE_UNKNOWN; 518 default: 519 return mPlayerType; 520 } 521 } 522 523 /** 524 * @hide 525 * Return the current state of the player linked to this configuration. The return value is one 526 * of {@link #PLAYER_STATE_IDLE}, {@link #PLAYER_STATE_PAUSED}, {@link #PLAYER_STATE_STARTED}, 527 * {@link #PLAYER_STATE_STOPPED}, {@link #PLAYER_STATE_RELEASED} or 528 * {@link #PLAYER_STATE_UNKNOWN}. 529 * @return the state of the player. 530 */ 531 @SystemApi getPlayerState()532 public @PlayerState int getPlayerState() { 533 return mPlayerState; 534 } 535 536 /** 537 * @hide 538 * Return an identifier unique for the lifetime of the player. 539 * @return a player interface identifier 540 */ 541 @SystemApi getPlayerInterfaceId()542 public int getPlayerInterfaceId() { 543 return mPlayerIId; 544 } 545 546 /** 547 * @hide 548 * Return a proxy for the player associated with this playback configuration 549 * @return a proxy player 550 */ 551 @SystemApi getPlayerProxy()552 public PlayerProxy getPlayerProxy() { 553 final IPlayerShell ips; 554 synchronized (this) { 555 ips = mIPlayerShell; 556 } 557 return ips == null ? null : new PlayerProxy(this); 558 } 559 560 /** 561 * @hide 562 * Return whether this player's output is being processed by the spatializer effect backing 563 * the {@link android.media.Spatializer} implementation. 564 * @return true if spatialized, false if not or playback hasn't started 565 */ 566 @SystemApi isSpatialized()567 public boolean isSpatialized() { 568 synchronized (mUpdateablePropLock) { 569 return mFormatInfo.mIsSpatialized; 570 } 571 } 572 573 /** 574 * @hide 575 * Return the sample rate in Hz of the content being played. 576 * @return the sample rate in Hertz, or 0 if playback hasn't started 577 */ 578 @SystemApi getSampleRate()579 public @IntRange(from = 0) int getSampleRate() { 580 synchronized (mUpdateablePropLock) { 581 return mFormatInfo.mSampleRate; 582 } 583 } 584 585 /** 586 * @hide 587 * Return the player's channel mask 588 * @return the channel mask, or 0 if playback hasn't started. See {@link AudioFormat} and 589 * the definitions for the <code>CHANNEL_OUT_*</code> values used for the mask's bitfield 590 */ 591 @SystemApi getChannelMask()592 public @AudioFormat.ChannelOut int getChannelMask() { 593 synchronized (mUpdateablePropLock) { 594 return (AudioFormat.convertNativeChannelMaskToOutMask(mFormatInfo.mNativeChannelMask)); 595 } 596 } 597 598 /** 599 * @hide 600 * @return the IPlayer interface for the associated player 601 */ getIPlayer()602 IPlayer getIPlayer() { 603 final IPlayerShell ips; 604 synchronized (this) { 605 ips = mIPlayerShell; 606 } 607 return ips == null ? null : ips.getIPlayer(); 608 } 609 610 /** 611 * @hide 612 * Handle a change of audio attributes 613 * @param attr 614 */ handleAudioAttributesEvent(@onNull AudioAttributes attr)615 public boolean handleAudioAttributesEvent(@NonNull AudioAttributes attr) { 616 final boolean changed = !attr.equals(mPlayerAttr); 617 mPlayerAttr = attr; 618 return changed; 619 } 620 621 /** 622 * @hide 623 * Handle a change of audio session ID 624 * @param sessionId the audio session ID 625 */ handleSessionIdEvent(int sessionId)626 public boolean handleSessionIdEvent(int sessionId) { 627 synchronized (mUpdateablePropLock) { 628 final boolean changed = sessionId != mSessionId; 629 mSessionId = sessionId; 630 return changed; 631 } 632 } 633 634 /** 635 * @hide 636 * Handle a change of the muted state 637 * @param mutedState the mute reason as a combination of {@link PlayerMuteEvent} flags 638 * @return true if the state changed, false otherwise 639 */ handleMutedEvent(@layerMuteEvent int mutedState)640 public boolean handleMutedEvent(@PlayerMuteEvent int mutedState) { 641 synchronized (mUpdateablePropLock) { 642 final boolean changed = mMutedState != mutedState; 643 mMutedState = mutedState; 644 return changed; 645 } 646 } 647 648 /** 649 * @hide 650 * Handle a change of playback format 651 * @param fi the latest format information 652 * @return true if the format changed, false otherwise 653 */ handleFormatEvent(@onNull FormatInfo fi)654 public boolean handleFormatEvent(@NonNull FormatInfo fi) { 655 synchronized (mUpdateablePropLock) { 656 final boolean changed = !mFormatInfo.equals(fi); 657 mFormatInfo = fi; 658 return changed; 659 } 660 } 661 662 /** 663 * @hide 664 * Handle a player state change 665 * @param event 666 * @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID} 667 * <br>Note device id is valid for {@code PLAYER_UPDATE_DEVICE_ID} or 668 * <br>{@code PLAYER_STATE_STARTED} events, as the device id will be reset to none when 669 * <br>pausing or stopping playback. It will be set to active device when playback starts or 670 * <br>it will be changed when PLAYER_UPDATE_DEVICE_ID is sent. The latter can happen if the 671 * <br>device changes in the middle of playback. 672 * @return true if the state changed, false otherwise 673 */ handleStateEvent(int event, int deviceId)674 public boolean handleStateEvent(int event, int deviceId) { 675 boolean changed = false; 676 synchronized (mUpdateablePropLock) { 677 678 // Do not update if it is only device id update 679 if (event != PLAYER_UPDATE_DEVICE_ID) { 680 changed = (mPlayerState != event); 681 mPlayerState = event; 682 } 683 684 if (event == PLAYER_STATE_STARTED || event == PLAYER_UPDATE_DEVICE_ID) { 685 changed = changed || (mDeviceId != deviceId); 686 mDeviceId = deviceId; 687 } 688 689 if (changed && (event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) { 690 mIPlayerShell.release(); 691 mIPlayerShell = null; 692 } 693 } 694 return changed; 695 } 696 697 // To report IPlayer death from death recipient 698 /** @hide */ 699 public interface PlayerDeathMonitor { playerDeath(int piid)700 public void playerDeath(int piid); 701 } 702 /** @hide */ 703 public static PlayerDeathMonitor sPlayerDeathMonitor; 704 playerDied()705 private void playerDied() { 706 if (sPlayerDeathMonitor != null) { 707 sPlayerDeathMonitor.playerDeath(mPlayerIId); 708 } 709 } 710 isMuteAffectingActiveState()711 private boolean isMuteAffectingActiveState() { 712 return (mMutedState & MUTED_BY_CLIENT_VOLUME) != 0 713 || (mMutedState & MUTED_BY_VOLUME_SHAPER) != 0 714 || (mMutedState & MUTED_BY_APP_OPS) != 0; 715 } 716 717 /** 718 * @hide 719 * Returns true if the player is considered "active", i.e. actively playing with unmuted 720 * volume, and thus in a state that should make it considered for the list public (sanitized) 721 * active playback configurations 722 * @return true if active 723 */ 724 @SystemApi isActive()725 public boolean isActive() { 726 switch (mPlayerState) { 727 case PLAYER_STATE_STARTED: 728 return !isMuteAffectingActiveState(); 729 case PLAYER_STATE_UNKNOWN: 730 case PLAYER_STATE_RELEASED: 731 case PLAYER_STATE_IDLE: 732 case PLAYER_STATE_PAUSED: 733 case PLAYER_STATE_STOPPED: 734 default: 735 return false; 736 } 737 } 738 739 /** 740 * @hide 741 * For AudioService dump 742 * @param pw 743 */ dump(PrintWriter pw)744 public void dump(PrintWriter pw) { 745 pw.println(" " + this); 746 } 747 748 public static final @android.annotation.NonNull Parcelable.Creator<AudioPlaybackConfiguration> CREATOR 749 = new Parcelable.Creator<AudioPlaybackConfiguration>() { 750 /** 751 * Rebuilds an AudioPlaybackConfiguration previously stored with writeToParcel(). 752 * @param p Parcel object to read the AudioPlaybackConfiguration from 753 * @return a new AudioPlaybackConfiguration created from the data in the parcel 754 */ 755 public AudioPlaybackConfiguration createFromParcel(Parcel p) { 756 return new AudioPlaybackConfiguration(p); 757 } 758 public AudioPlaybackConfiguration[] newArray(int size) { 759 return new AudioPlaybackConfiguration[size]; 760 } 761 }; 762 763 @Override hashCode()764 public int hashCode() { 765 synchronized (mUpdateablePropLock) { 766 return Objects.hash(mPlayerIId, mDeviceId, mMutedState, mPlayerType, mClientUid, 767 mClientPid, mSessionId); 768 } 769 } 770 771 @Override describeContents()772 public int describeContents() { 773 return 0; 774 } 775 776 @Override writeToParcel(Parcel dest, int flags)777 public void writeToParcel(Parcel dest, int flags) { 778 synchronized (mUpdateablePropLock) { 779 dest.writeInt(mPlayerIId); 780 dest.writeInt(mDeviceId); 781 dest.writeInt(mMutedState); 782 dest.writeInt(mPlayerType); 783 dest.writeInt(mClientUid); 784 dest.writeInt(mClientPid); 785 dest.writeInt(mPlayerState); 786 mPlayerAttr.writeToParcel(dest, 0); 787 final IPlayerShell ips; 788 synchronized (this) { 789 ips = mIPlayerShell; 790 } 791 dest.writeStrongInterface(ips == null ? null : ips.getIPlayer()); 792 dest.writeInt(mSessionId); 793 mFormatInfo.writeToParcel(dest, 0); 794 } 795 } 796 AudioPlaybackConfiguration(Parcel in)797 private AudioPlaybackConfiguration(Parcel in) { 798 mPlayerIId = in.readInt(); 799 mDeviceId = in.readInt(); 800 mMutedState = in.readInt(); 801 mPlayerType = in.readInt(); 802 mClientUid = in.readInt(); 803 mClientPid = in.readInt(); 804 mPlayerState = in.readInt(); 805 mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in); 806 final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder()); 807 mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p); 808 mSessionId = in.readInt(); 809 mFormatInfo = FormatInfo.CREATOR.createFromParcel(in); 810 } 811 812 @Override equals(Object o)813 public boolean equals(Object o) { 814 if (this == o) return true; 815 if (o == null || !(o instanceof AudioPlaybackConfiguration)) return false; 816 817 AudioPlaybackConfiguration that = (AudioPlaybackConfiguration) o; 818 819 return ((mPlayerIId == that.mPlayerIId) 820 && (mDeviceId == that.mDeviceId) 821 && (mMutedState == that.mMutedState) 822 && (mPlayerType == that.mPlayerType) 823 && (mClientUid == that.mClientUid) 824 && (mClientPid == that.mClientPid)) 825 && (mSessionId == that.mSessionId); 826 } 827 828 @Override toString()829 public String toString() { 830 StringBuilder apcToString = new StringBuilder(); 831 synchronized (mUpdateablePropLock) { 832 apcToString.append("AudioPlaybackConfiguration piid:").append(mPlayerIId).append( 833 " deviceId:").append(mDeviceId).append(" type:").append( 834 toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append( 835 mClientUid).append( 836 "/").append(mClientPid).append(" state:").append( 837 toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append( 838 mPlayerAttr).append( 839 " sessionId:").append(mSessionId).append(" mutedState:"); 840 if (mMutedState == 0) { 841 apcToString.append("none "); 842 } else { 843 if ((mMutedState & MUTED_BY_MASTER) != 0) { 844 apcToString.append("master "); 845 } 846 if ((mMutedState & MUTED_BY_STREAM_VOLUME) != 0) { 847 apcToString.append("streamVolume "); 848 } 849 if ((mMutedState & MUTED_BY_STREAM_MUTED) != 0) { 850 apcToString.append("streamMute "); 851 } 852 if ((mMutedState & MUTED_BY_APP_OPS) != 0) { 853 apcToString.append("appOps "); 854 } 855 if ((mMutedState & MUTED_BY_CLIENT_VOLUME) != 0) { 856 apcToString.append("clientVolume "); 857 } 858 if ((mMutedState & MUTED_BY_VOLUME_SHAPER) != 0) { 859 apcToString.append("volumeShaper "); 860 } 861 } 862 apcToString.append(" ").append(mFormatInfo); 863 } 864 return apcToString.toString(); 865 } 866 867 //===================================================================== 868 // Inner class for corresponding IPlayer and its death monitoring 869 static final class IPlayerShell implements IBinder.DeathRecipient { 870 871 final AudioPlaybackConfiguration mMonitor; // never null 872 private volatile IPlayer mIPlayer; 873 IPlayerShell(@onNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer)874 IPlayerShell(@NonNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer) { 875 mMonitor = monitor; 876 mIPlayer = iplayer; 877 } 878 monitorDeath()879 synchronized void monitorDeath() { 880 if (mIPlayer == null) { 881 return; 882 } 883 try { 884 mIPlayer.asBinder().linkToDeath(this, 0); 885 } catch (RemoteException e) { 886 if (mMonitor != null) { 887 Log.w(TAG, "Could not link to client death for piid=" + mMonitor.mPlayerIId, e); 888 } else { 889 Log.w(TAG, "Could not link to client death", e); 890 } 891 } 892 } 893 getIPlayer()894 IPlayer getIPlayer() { 895 return mIPlayer; 896 } 897 binderDied()898 public void binderDied() { 899 if (mMonitor != null) { 900 if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied for piid=" + mMonitor.mPlayerIId);} 901 mMonitor.playerDied(); 902 } else if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied"); } 903 } 904 release()905 synchronized void release() { 906 if (mIPlayer == null) { 907 return; 908 } 909 mIPlayer.asBinder().unlinkToDeath(this, 0); 910 mIPlayer = null; 911 Binder.flushPendingCommands(); 912 } 913 } 914 915 //===================================================================== 916 917 /** 918 * @hide 919 * Class to store sample rate, channel mask, and spatialization status 920 */ 921 public static final class FormatInfo implements Parcelable { 922 static final FormatInfo DEFAULT = new FormatInfo( 923 /*spatialized*/ false, /*channel mask*/ 0, /*sample rate*/ 0); 924 final boolean mIsSpatialized; 925 final int mNativeChannelMask; 926 final int mSampleRate; 927 FormatInfo(boolean isSpatialized, int nativeChannelMask, int sampleRate)928 public FormatInfo(boolean isSpatialized, int nativeChannelMask, int sampleRate) { 929 mIsSpatialized = isSpatialized; 930 mNativeChannelMask = nativeChannelMask; 931 mSampleRate = sampleRate; 932 } 933 934 @Override toString()935 public String toString() { 936 return "FormatInfo{" 937 + "isSpatialized=" + mIsSpatialized 938 + ", channelMask=0x" + Integer.toHexString(mNativeChannelMask) 939 + ", sampleRate=" + mSampleRate 940 + '}'; 941 } 942 943 @Override equals(Object o)944 public boolean equals(Object o) { 945 if (this == o) return true; 946 if (!(o instanceof FormatInfo)) return false; 947 FormatInfo that = (FormatInfo) o; 948 return mIsSpatialized == that.mIsSpatialized 949 && mNativeChannelMask == that.mNativeChannelMask 950 && mSampleRate == that.mSampleRate; 951 } 952 953 @Override hashCode()954 public int hashCode() { 955 return Objects.hash(mIsSpatialized, mNativeChannelMask, mSampleRate); 956 } 957 958 @Override describeContents()959 public int describeContents() { 960 return 0; 961 } 962 963 @Override writeToParcel(@ndroidx.annotation.NonNull Parcel dest, int flags)964 public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) { 965 dest.writeBoolean(mIsSpatialized); 966 dest.writeInt(mNativeChannelMask); 967 dest.writeInt(mSampleRate); 968 } 969 FormatInfo(Parcel in)970 private FormatInfo(Parcel in) { 971 this( 972 in.readBoolean(), // isSpatialized 973 in.readInt(), // channelMask 974 in.readInt() // sampleRate 975 ); 976 } 977 978 public static final @NonNull Parcelable.Creator<FormatInfo> CREATOR = 979 new Parcelable.Creator<FormatInfo>() { 980 public FormatInfo createFromParcel(Parcel p) { 981 return new FormatInfo(p); 982 } 983 public FormatInfo[] newArray(int size) { 984 return new FormatInfo[size]; 985 } 986 }; 987 } 988 //===================================================================== 989 // Utilities 990 991 /** @hide */ toLogFriendlyPlayerType(int type)992 public static String toLogFriendlyPlayerType(int type) { 993 switch (type) { 994 case PLAYER_TYPE_UNKNOWN: return "unknown"; 995 case PLAYER_TYPE_JAM_AUDIOTRACK: return "android.media.AudioTrack"; 996 case PLAYER_TYPE_JAM_MEDIAPLAYER: return "android.media.MediaPlayer"; 997 case PLAYER_TYPE_JAM_SOUNDPOOL: return "android.media.SoundPool"; 998 case PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE: 999 return "OpenSL ES AudioPlayer (Buffer Queue)"; 1000 case PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD: 1001 return "OpenSL ES AudioPlayer (URI/FD)"; 1002 case PLAYER_TYPE_AAUDIO: return "AAudio"; 1003 case PLAYER_TYPE_HW_SOURCE: return "hardware source"; 1004 case PLAYER_TYPE_EXTERNAL_PROXY: return "external proxy"; 1005 default: 1006 return "unknown player type " + type + " - FIXME"; 1007 } 1008 } 1009 1010 /** @hide */ toLogFriendlyPlayerState(int state)1011 public static String toLogFriendlyPlayerState(int state) { 1012 switch (state) { 1013 case PLAYER_STATE_UNKNOWN: return "unknown"; 1014 case PLAYER_STATE_RELEASED: return "released"; 1015 case PLAYER_STATE_IDLE: return "idle"; 1016 case PLAYER_STATE_STARTED: return "started"; 1017 case PLAYER_STATE_PAUSED: return "paused"; 1018 case PLAYER_STATE_STOPPED: return "stopped"; 1019 case PLAYER_UPDATE_DEVICE_ID: return "device updated"; 1020 case PLAYER_UPDATE_PORT_ID: return "port updated"; 1021 case PLAYER_UPDATE_MUTED: return "muted updated"; 1022 default: 1023 return "unknown player state - FIXME"; 1024 } 1025 } 1026 } 1027