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