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