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 */ isActive()403 public boolean isActive() { 404 switch (mPlayerState) { 405 case PLAYER_STATE_STARTED: 406 return true; 407 case PLAYER_STATE_UNKNOWN: 408 case PLAYER_STATE_RELEASED: 409 case PLAYER_STATE_IDLE: 410 case PLAYER_STATE_PAUSED: 411 case PLAYER_STATE_STOPPED: 412 default: 413 return false; 414 } 415 } 416 417 /** 418 * @hide 419 * For AudioService dump 420 * @param pw 421 */ dump(PrintWriter pw)422 public void dump(PrintWriter pw) { 423 pw.println(" " + toLogFriendlyString(this)); 424 } 425 426 /** 427 * @hide 428 */ toLogFriendlyString(AudioPlaybackConfiguration apc)429 public static String toLogFriendlyString(AudioPlaybackConfiguration apc) { 430 return new String("ID:" + apc.mPlayerIId 431 + " -- type:" + toLogFriendlyPlayerType(apc.mPlayerType) 432 + " -- u/pid:" + apc.mClientUid +"/" + apc.mClientPid 433 + " -- state:" + toLogFriendlyPlayerState(apc.mPlayerState) 434 + " -- attr:" + apc.mPlayerAttr); 435 } 436 437 public static final @android.annotation.NonNull Parcelable.Creator<AudioPlaybackConfiguration> CREATOR 438 = new Parcelable.Creator<AudioPlaybackConfiguration>() { 439 /** 440 * Rebuilds an AudioPlaybackConfiguration previously stored with writeToParcel(). 441 * @param p Parcel object to read the AudioPlaybackConfiguration from 442 * @return a new AudioPlaybackConfiguration created from the data in the parcel 443 */ 444 public AudioPlaybackConfiguration createFromParcel(Parcel p) { 445 return new AudioPlaybackConfiguration(p); 446 } 447 public AudioPlaybackConfiguration[] newArray(int size) { 448 return new AudioPlaybackConfiguration[size]; 449 } 450 }; 451 452 @Override hashCode()453 public int hashCode() { 454 return Objects.hash(mPlayerIId, mPlayerType, mClientUid, mClientPid); 455 } 456 457 @Override describeContents()458 public int describeContents() { 459 return 0; 460 } 461 462 @Override writeToParcel(Parcel dest, int flags)463 public void writeToParcel(Parcel dest, int flags) { 464 dest.writeInt(mPlayerIId); 465 dest.writeInt(mPlayerType); 466 dest.writeInt(mClientUid); 467 dest.writeInt(mClientPid); 468 dest.writeInt(mPlayerState); 469 mPlayerAttr.writeToParcel(dest, 0); 470 final IPlayerShell ips; 471 synchronized (this) { 472 ips = mIPlayerShell; 473 } 474 dest.writeStrongInterface(ips == null ? null : ips.getIPlayer()); 475 } 476 AudioPlaybackConfiguration(Parcel in)477 private AudioPlaybackConfiguration(Parcel in) { 478 mPlayerIId = in.readInt(); 479 mPlayerType = in.readInt(); 480 mClientUid = in.readInt(); 481 mClientPid = in.readInt(); 482 mPlayerState = in.readInt(); 483 mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in); 484 final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder()); 485 mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p); 486 } 487 488 @Override equals(Object o)489 public boolean equals(Object o) { 490 if (this == o) return true; 491 if (o == null || !(o instanceof AudioPlaybackConfiguration)) return false; 492 493 AudioPlaybackConfiguration that = (AudioPlaybackConfiguration) o; 494 495 return ((mPlayerIId == that.mPlayerIId) 496 && (mPlayerType == that.mPlayerType) 497 && (mClientUid == that.mClientUid) 498 && (mClientPid == that.mClientPid)); 499 } 500 501 //===================================================================== 502 // Inner class for corresponding IPlayer and its death monitoring 503 static final class IPlayerShell implements IBinder.DeathRecipient { 504 505 final AudioPlaybackConfiguration mMonitor; // never null 506 private volatile IPlayer mIPlayer; 507 IPlayerShell(@onNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer)508 IPlayerShell(@NonNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer) { 509 mMonitor = monitor; 510 mIPlayer = iplayer; 511 } 512 monitorDeath()513 synchronized void monitorDeath() { 514 if (mIPlayer == null) { 515 return; 516 } 517 try { 518 mIPlayer.asBinder().linkToDeath(this, 0); 519 } catch (RemoteException e) { 520 if (mMonitor != null) { 521 Log.w(TAG, "Could not link to client death for piid=" + mMonitor.mPlayerIId, e); 522 } else { 523 Log.w(TAG, "Could not link to client death", e); 524 } 525 } 526 } 527 getIPlayer()528 IPlayer getIPlayer() { 529 return mIPlayer; 530 } 531 binderDied()532 public void binderDied() { 533 if (mMonitor != null) { 534 if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied for piid=" + mMonitor.mPlayerIId);} 535 mMonitor.playerDied(); 536 } else if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied"); } 537 } 538 release()539 synchronized void release() { 540 if (mIPlayer == null) { 541 return; 542 } 543 mIPlayer.asBinder().unlinkToDeath(this, 0); 544 mIPlayer = null; 545 Binder.flushPendingCommands(); 546 } 547 } 548 549 //===================================================================== 550 // Utilities 551 552 /** @hide */ toLogFriendlyPlayerType(int type)553 public static String toLogFriendlyPlayerType(int type) { 554 switch (type) { 555 case PLAYER_TYPE_UNKNOWN: return "unknown"; 556 case PLAYER_TYPE_JAM_AUDIOTRACK: return "android.media.AudioTrack"; 557 case PLAYER_TYPE_JAM_MEDIAPLAYER: return "android.media.MediaPlayer"; 558 case PLAYER_TYPE_JAM_SOUNDPOOL: return "android.media.SoundPool"; 559 case PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE: 560 return "OpenSL ES AudioPlayer (Buffer Queue)"; 561 case PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD: 562 return "OpenSL ES AudioPlayer (URI/FD)"; 563 case PLAYER_TYPE_AAUDIO: return "AAudio"; 564 case PLAYER_TYPE_HW_SOURCE: return "hardware source"; 565 case PLAYER_TYPE_EXTERNAL_PROXY: return "external proxy"; 566 default: 567 return "unknown player type " + type + " - FIXME"; 568 } 569 } 570 571 /** @hide */ toLogFriendlyPlayerState(int state)572 public static String toLogFriendlyPlayerState(int state) { 573 switch (state) { 574 case PLAYER_STATE_UNKNOWN: return "unknown"; 575 case PLAYER_STATE_RELEASED: return "released"; 576 case PLAYER_STATE_IDLE: return "idle"; 577 case PLAYER_STATE_STARTED: return "started"; 578 case PLAYER_STATE_PAUSED: return "paused"; 579 case PLAYER_STATE_STOPPED: return "stopped"; 580 default: 581 return "unknown player state - FIXME"; 582 } 583 } 584 } 585