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.NonNull; 24 import android.annotation.SystemApi; 25 import android.os.Binder; 26 import android.os.IBinder; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 import android.os.RemoteException; 30 import android.util.Log; 31 32 import java.io.PrintWriter; 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.util.Objects; 36 37 /** 38 * The AudioPlaybackConfiguration class collects the information describing an audio playback 39 * session. 40 */ 41 public final class AudioPlaybackConfiguration implements Parcelable { 42 private static final String TAG = new String("AudioPlaybackConfiguration"); 43 44 private static final boolean DEBUG = false; 45 46 /** @hide */ 47 public static final int PLAYER_PIID_INVALID = -1; 48 /** @hide */ 49 public static final int PLAYER_UPID_INVALID = -1; 50 51 // information about the implementation 52 /** 53 * @hide 54 * An unknown type of player 55 */ 56 @SystemApi 57 public static final int PLAYER_TYPE_UNKNOWN = -1; 58 /** 59 * @hide 60 * Player backed by a java android.media.AudioTrack player 61 */ 62 @SystemApi 63 public static final int PLAYER_TYPE_JAM_AUDIOTRACK = 1; 64 /** 65 * @hide 66 * Player backed by a java android.media.MediaPlayer player 67 */ 68 @SystemApi 69 public static final int PLAYER_TYPE_JAM_MEDIAPLAYER = 2; 70 /** 71 * @hide 72 * Player backed by a java android.media.SoundPool player 73 */ 74 @SystemApi 75 public static final int PLAYER_TYPE_JAM_SOUNDPOOL = 3; 76 /** 77 * @hide 78 * Player backed by a C OpenSL ES AudioPlayer player with a BufferQueue source 79 */ 80 @SystemApi 81 public static final int PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11; 82 /** 83 * @hide 84 * Player backed by a C OpenSL ES AudioPlayer player with a URI or FD source 85 */ 86 @SystemApi 87 public static final int PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12; 88 89 /** 90 * @hide 91 * Player backed an AAudio player. 92 * Note this type is not in System API so it will not be returned in public API calls 93 */ 94 // TODO unhide for SystemApi, update getPlayerType() 95 public static final int PLAYER_TYPE_AAUDIO = 13; 96 97 /** 98 * @hide 99 * Player backed a hardware source, whose state is visible in the Android audio policy manager. 100 * Note this type is not in System API so it will not be returned in public API calls 101 */ 102 // TODO unhide for SystemApi, update getPlayerType() 103 public static final int PLAYER_TYPE_HW_SOURCE = 14; 104 105 /** 106 * @hide 107 * Player is a proxy for an audio player whose audio and state doesn't go through the Android 108 * audio framework. 109 * Note this type is not in System API so it will not be returned in public API calls 110 */ 111 // TODO unhide for SystemApi, update getPlayerType() 112 public static final int PLAYER_TYPE_EXTERNAL_PROXY = 15; 113 114 /** @hide */ 115 @IntDef({ 116 PLAYER_TYPE_UNKNOWN, 117 PLAYER_TYPE_JAM_AUDIOTRACK, 118 PLAYER_TYPE_JAM_MEDIAPLAYER, 119 PLAYER_TYPE_JAM_SOUNDPOOL, 120 PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE, 121 PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD, 122 }) 123 @Retention(RetentionPolicy.SOURCE) 124 public @interface PlayerType {} 125 126 /** 127 * @hide 128 * An unknown player state 129 */ 130 @SystemApi 131 public static final int PLAYER_STATE_UNKNOWN = -1; 132 /** 133 * @hide 134 * The resources of the player have been released, it cannot play anymore 135 */ 136 @SystemApi 137 public static final int PLAYER_STATE_RELEASED = 0; 138 /** 139 * @hide 140 * The state of a player when it's created 141 */ 142 @SystemApi 143 public static final int PLAYER_STATE_IDLE = 1; 144 /** 145 * @hide 146 * The state of a player that is actively playing 147 */ 148 @SystemApi 149 public static final int PLAYER_STATE_STARTED = 2; 150 /** 151 * @hide 152 * The state of a player where playback is paused 153 */ 154 @SystemApi 155 public static final int PLAYER_STATE_PAUSED = 3; 156 /** 157 * @hide 158 * The state of a player where playback is stopped 159 */ 160 @SystemApi 161 public static final int PLAYER_STATE_STOPPED = 4; 162 163 /** @hide */ 164 @IntDef({ 165 PLAYER_STATE_UNKNOWN, 166 PLAYER_STATE_RELEASED, 167 PLAYER_STATE_IDLE, 168 PLAYER_STATE_STARTED, 169 PLAYER_STATE_PAUSED, 170 PLAYER_STATE_STOPPED 171 }) 172 @Retention(RetentionPolicy.SOURCE) 173 public @interface PlayerState {} 174 175 // immutable data 176 private final int mPlayerIId; 177 178 // not final due to anonymization step 179 private int mPlayerType; 180 private int mClientUid; 181 private int mClientPid; 182 // the IPlayer reference and death monitor 183 private IPlayerShell mIPlayerShell; 184 185 private int mPlayerState; 186 private AudioAttributes mPlayerAttr; // never null 187 188 /** 189 * Never use without initializing parameters afterwards 190 */ AudioPlaybackConfiguration(int piid)191 private AudioPlaybackConfiguration(int piid) { 192 mPlayerIId = piid; 193 mIPlayerShell = null; 194 } 195 196 /** 197 * @hide 198 */ AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid)199 public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) { 200 if (DEBUG) { Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer); } 201 mPlayerIId = piid; 202 mPlayerType = pic.mPlayerType; 203 mClientUid = uid; 204 mClientPid = pid; 205 mPlayerState = PLAYER_STATE_IDLE; 206 mPlayerAttr = pic.mAttributes; 207 if ((sPlayerDeathMonitor != null) && (pic.mIPlayer != null)) { 208 mIPlayerShell = new IPlayerShell(this, pic.mIPlayer); 209 } else { 210 mIPlayerShell = null; 211 } 212 } 213 214 /** 215 * @hide 216 */ init()217 public void init() { 218 synchronized (this) { 219 if (mIPlayerShell != null) { 220 mIPlayerShell.monitorDeath(); 221 } 222 } 223 } 224 225 // Note that this method is called server side, so no "privileged" information is ever sent 226 // to a client that is not supposed to have access to it. 227 /** 228 * @hide 229 * Creates a copy of the playback configuration that is stripped of any data enabling 230 * identification of which application it is associated with ("anonymized"). 231 * @param toSanitize 232 */ anonymizedCopy(AudioPlaybackConfiguration in)233 public static AudioPlaybackConfiguration anonymizedCopy(AudioPlaybackConfiguration in) { 234 final AudioPlaybackConfiguration anonymCopy = new AudioPlaybackConfiguration(in.mPlayerIId); 235 anonymCopy.mPlayerState = in.mPlayerState; 236 // do not reuse the full attributes: only usage, content type and public flags are allowed 237 anonymCopy.mPlayerAttr = new AudioAttributes.Builder() 238 .setUsage(in.mPlayerAttr.getUsage()) 239 .setContentType(in.mPlayerAttr.getContentType()) 240 .setFlags(in.mPlayerAttr.getFlags()) 241 .setAllowedCapturePolicy( 242 in.mPlayerAttr.getAllowedCapturePolicy() == ALLOW_CAPTURE_BY_ALL 243 ? ALLOW_CAPTURE_BY_ALL : ALLOW_CAPTURE_BY_NONE) 244 .build(); 245 // anonymized data 246 anonymCopy.mPlayerType = PLAYER_TYPE_UNKNOWN; 247 anonymCopy.mClientUid = PLAYER_UPID_INVALID; 248 anonymCopy.mClientPid = PLAYER_UPID_INVALID; 249 anonymCopy.mIPlayerShell = null; 250 return anonymCopy; 251 } 252 253 /** 254 * Return the {@link AudioAttributes} of the corresponding player. 255 * @return the audio attributes of the player 256 */ getAudioAttributes()257 public AudioAttributes getAudioAttributes() { 258 return mPlayerAttr; 259 } 260 261 /** 262 * @hide 263 * Return the uid of the client application that created this player. 264 * @return the uid of the client 265 */ 266 @SystemApi getClientUid()267 public int getClientUid() { 268 return mClientUid; 269 } 270 271 /** 272 * @hide 273 * Return the pid of the client application that created this player. 274 * @return the pid of the client 275 */ 276 @SystemApi getClientPid()277 public int getClientPid() { 278 return mClientPid; 279 } 280 281 /** 282 * @hide 283 * Return the type of player linked to this configuration. The return value is one of 284 * {@link #PLAYER_TYPE_JAM_AUDIOTRACK}, {@link #PLAYER_TYPE_JAM_MEDIAPLAYER}, 285 * {@link #PLAYER_TYPE_JAM_SOUNDPOOL}, {@link #PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE}, 286 * {@link #PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD}, or {@link #PLAYER_TYPE_UNKNOWN}. 287 * <br>Note that player types not exposed in the system API will be represented as 288 * {@link #PLAYER_TYPE_UNKNOWN}. 289 * @return the type of the player. 290 */ 291 @SystemApi getPlayerType()292 public @PlayerType int getPlayerType() { 293 switch (mPlayerType) { 294 case PLAYER_TYPE_AAUDIO: 295 case PLAYER_TYPE_HW_SOURCE: 296 case PLAYER_TYPE_EXTERNAL_PROXY: 297 return PLAYER_TYPE_UNKNOWN; 298 default: 299 return mPlayerType; 300 } 301 } 302 303 /** 304 * @hide 305 * Return the current state of the player linked to this configuration. The return value is one 306 * of {@link #PLAYER_STATE_IDLE}, {@link #PLAYER_STATE_PAUSED}, {@link #PLAYER_STATE_STARTED}, 307 * {@link #PLAYER_STATE_STOPPED}, {@link #PLAYER_STATE_RELEASED} or 308 * {@link #PLAYER_STATE_UNKNOWN}. 309 * @return the state of the player. 310 */ 311 @SystemApi getPlayerState()312 public @PlayerState int getPlayerState() { 313 return mPlayerState; 314 } 315 316 /** 317 * @hide 318 * Return an identifier unique for the lifetime of the player. 319 * @return a player interface identifier 320 */ 321 @SystemApi getPlayerInterfaceId()322 public int getPlayerInterfaceId() { 323 return mPlayerIId; 324 } 325 326 /** 327 * @hide 328 * Return a proxy for the player associated with this playback configuration 329 * @return a proxy player 330 */ 331 @SystemApi getPlayerProxy()332 public PlayerProxy getPlayerProxy() { 333 final IPlayerShell ips; 334 synchronized (this) { 335 ips = mIPlayerShell; 336 } 337 return ips == null ? null : new PlayerProxy(this); 338 } 339 340 /** 341 * @hide 342 * @return the IPlayer interface for the associated player 343 */ getIPlayer()344 IPlayer getIPlayer() { 345 final IPlayerShell ips; 346 synchronized (this) { 347 ips = mIPlayerShell; 348 } 349 return ips == null ? null : ips.getIPlayer(); 350 } 351 352 /** 353 * @hide 354 * Handle a change of audio attributes 355 * @param attr 356 */ handleAudioAttributesEvent(@onNull AudioAttributes attr)357 public boolean handleAudioAttributesEvent(@NonNull AudioAttributes attr) { 358 final boolean changed = !attr.equals(mPlayerAttr); 359 mPlayerAttr = attr; 360 return changed; 361 } 362 363 /** 364 * @hide 365 * Handle a player state change 366 * @param event 367 * @return true if the state changed, false otherwise 368 */ handleStateEvent(int event)369 public boolean handleStateEvent(int event) { 370 final boolean changed; 371 synchronized (this) { 372 changed = (mPlayerState != event); 373 mPlayerState = event; 374 if (changed && (event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) { 375 mIPlayerShell.release(); 376 mIPlayerShell = null; 377 } 378 } 379 return changed; 380 } 381 382 // To report IPlayer death from death recipient 383 /** @hide */ 384 public interface PlayerDeathMonitor { playerDeath(int piid)385 public void playerDeath(int piid); 386 } 387 /** @hide */ 388 public static PlayerDeathMonitor sPlayerDeathMonitor; 389 playerDied()390 private void playerDied() { 391 if (sPlayerDeathMonitor != null) { 392 sPlayerDeathMonitor.playerDeath(mPlayerIId); 393 } 394 } 395 396 /** 397 * @hide 398 * Returns true if the player is considered "active", i.e. actively playing, and thus 399 * in a state that should make it considered for the list public (sanitized) active playback 400 * configurations 401 * @return true if active 402 */ 403 @SystemApi isActive()404 public boolean isActive() { 405 switch (mPlayerState) { 406 case PLAYER_STATE_STARTED: 407 return true; 408 case PLAYER_STATE_UNKNOWN: 409 case PLAYER_STATE_RELEASED: 410 case PLAYER_STATE_IDLE: 411 case PLAYER_STATE_PAUSED: 412 case PLAYER_STATE_STOPPED: 413 default: 414 return false; 415 } 416 } 417 418 /** 419 * @hide 420 * For AudioService dump 421 * @param pw 422 */ dump(PrintWriter pw)423 public void dump(PrintWriter pw) { 424 pw.println(" " + this); 425 } 426 427 public static final @android.annotation.NonNull Parcelable.Creator<AudioPlaybackConfiguration> CREATOR 428 = new Parcelable.Creator<AudioPlaybackConfiguration>() { 429 /** 430 * Rebuilds an AudioPlaybackConfiguration previously stored with writeToParcel(). 431 * @param p Parcel object to read the AudioPlaybackConfiguration from 432 * @return a new AudioPlaybackConfiguration created from the data in the parcel 433 */ 434 public AudioPlaybackConfiguration createFromParcel(Parcel p) { 435 return new AudioPlaybackConfiguration(p); 436 } 437 public AudioPlaybackConfiguration[] newArray(int size) { 438 return new AudioPlaybackConfiguration[size]; 439 } 440 }; 441 442 @Override hashCode()443 public int hashCode() { 444 return Objects.hash(mPlayerIId, mPlayerType, mClientUid, mClientPid); 445 } 446 447 @Override describeContents()448 public int describeContents() { 449 return 0; 450 } 451 452 @Override writeToParcel(Parcel dest, int flags)453 public void writeToParcel(Parcel dest, int flags) { 454 dest.writeInt(mPlayerIId); 455 dest.writeInt(mPlayerType); 456 dest.writeInt(mClientUid); 457 dest.writeInt(mClientPid); 458 dest.writeInt(mPlayerState); 459 mPlayerAttr.writeToParcel(dest, 0); 460 final IPlayerShell ips; 461 synchronized (this) { 462 ips = mIPlayerShell; 463 } 464 dest.writeStrongInterface(ips == null ? null : ips.getIPlayer()); 465 } 466 AudioPlaybackConfiguration(Parcel in)467 private AudioPlaybackConfiguration(Parcel in) { 468 mPlayerIId = in.readInt(); 469 mPlayerType = in.readInt(); 470 mClientUid = in.readInt(); 471 mClientPid = in.readInt(); 472 mPlayerState = in.readInt(); 473 mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in); 474 final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder()); 475 mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p); 476 } 477 478 @Override equals(Object o)479 public boolean equals(Object o) { 480 if (this == o) return true; 481 if (o == null || !(o instanceof AudioPlaybackConfiguration)) return false; 482 483 AudioPlaybackConfiguration that = (AudioPlaybackConfiguration) o; 484 485 return ((mPlayerIId == that.mPlayerIId) 486 && (mPlayerType == that.mPlayerType) 487 && (mClientUid == that.mClientUid) 488 && (mClientPid == that.mClientPid)); 489 } 490 491 @Override toString()492 public String toString() { 493 return "AudioPlaybackConfiguration piid:" + mPlayerIId 494 + " type:" + toLogFriendlyPlayerType(mPlayerType) 495 + " u/pid:" + mClientUid + "/" + mClientPid 496 + " state:" + toLogFriendlyPlayerState(mPlayerState) 497 + " attr:" + mPlayerAttr; 498 } 499 500 //===================================================================== 501 // Inner class for corresponding IPlayer and its death monitoring 502 static final class IPlayerShell implements IBinder.DeathRecipient { 503 504 final AudioPlaybackConfiguration mMonitor; // never null 505 private volatile IPlayer mIPlayer; 506 IPlayerShell(@onNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer)507 IPlayerShell(@NonNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer) { 508 mMonitor = monitor; 509 mIPlayer = iplayer; 510 } 511 monitorDeath()512 synchronized void monitorDeath() { 513 if (mIPlayer == null) { 514 return; 515 } 516 try { 517 mIPlayer.asBinder().linkToDeath(this, 0); 518 } catch (RemoteException e) { 519 if (mMonitor != null) { 520 Log.w(TAG, "Could not link to client death for piid=" + mMonitor.mPlayerIId, e); 521 } else { 522 Log.w(TAG, "Could not link to client death", e); 523 } 524 } 525 } 526 getIPlayer()527 IPlayer getIPlayer() { 528 return mIPlayer; 529 } 530 binderDied()531 public void binderDied() { 532 if (mMonitor != null) { 533 if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied for piid=" + mMonitor.mPlayerIId);} 534 mMonitor.playerDied(); 535 } else if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied"); } 536 } 537 release()538 synchronized void release() { 539 if (mIPlayer == null) { 540 return; 541 } 542 mIPlayer.asBinder().unlinkToDeath(this, 0); 543 mIPlayer = null; 544 Binder.flushPendingCommands(); 545 } 546 } 547 548 //===================================================================== 549 // Utilities 550 551 /** @hide */ toLogFriendlyPlayerType(int type)552 public static String toLogFriendlyPlayerType(int type) { 553 switch (type) { 554 case PLAYER_TYPE_UNKNOWN: return "unknown"; 555 case PLAYER_TYPE_JAM_AUDIOTRACK: return "android.media.AudioTrack"; 556 case PLAYER_TYPE_JAM_MEDIAPLAYER: return "android.media.MediaPlayer"; 557 case PLAYER_TYPE_JAM_SOUNDPOOL: return "android.media.SoundPool"; 558 case PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE: 559 return "OpenSL ES AudioPlayer (Buffer Queue)"; 560 case PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD: 561 return "OpenSL ES AudioPlayer (URI/FD)"; 562 case PLAYER_TYPE_AAUDIO: return "AAudio"; 563 case PLAYER_TYPE_HW_SOURCE: return "hardware source"; 564 case PLAYER_TYPE_EXTERNAL_PROXY: return "external proxy"; 565 default: 566 return "unknown player type " + type + " - FIXME"; 567 } 568 } 569 570 /** @hide */ toLogFriendlyPlayerState(int state)571 public static String toLogFriendlyPlayerState(int state) { 572 switch (state) { 573 case PLAYER_STATE_UNKNOWN: return "unknown"; 574 case PLAYER_STATE_RELEASED: return "released"; 575 case PLAYER_STATE_IDLE: return "idle"; 576 case PLAYER_STATE_STARTED: return "started"; 577 case PLAYER_STATE_PAUSED: return "paused"; 578 case PLAYER_STATE_STOPPED: return "stopped"; 579 default: 580 return "unknown player state - FIXME"; 581 } 582 } 583 } 584