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