blob: 7dfdb20a98fe296a0d77d4aba646fdeb56848c56 [file] [log] [blame]
Justin Klaassen10d07c82017-09-15 17:58:39 -04001/*
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 */
Justin Klaassenb8042fc2018-04-15 00:41:15 -040046 public static final int PLAYER_PIID_UNASSIGNED = 0;
47 /** @hide */
Justin Klaassen10d07c82017-09-15 17:58:39 -040048 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 */
190 private AudioPlaybackConfiguration(int piid) {
191 mPlayerIId = piid;
192 mIPlayerShell = null;
193 }
194
195 /**
196 * @hide
197 */
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 */
216 public void init() {
Justin Klaassenbc81c7a2017-09-18 17:38:50 -0400217 synchronized (this) {
218 if (mIPlayerShell != null) {
219 mIPlayerShell.monitorDeath();
220 }
Justin Klaassen10d07c82017-09-15 17:58:39 -0400221 }
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 */
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 */
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
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
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
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
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
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
328 public PlayerProxy getPlayerProxy() {
Justin Klaassenbc81c7a2017-09-18 17:38:50 -0400329 final IPlayerShell ips;
330 synchronized (this) {
331 ips = mIPlayerShell;
332 }
333 return ips == null ? null : new PlayerProxy(this);
Justin Klaassen10d07c82017-09-15 17:58:39 -0400334 }
335
336 /**
337 * @hide
338 * @return the IPlayer interface for the associated player
339 */
340 IPlayer getIPlayer() {
Justin Klaassenbc81c7a2017-09-18 17:38:50 -0400341 final IPlayerShell ips;
342 synchronized (this) {
343 ips = mIPlayerShell;
344 }
345 return ips == null ? null : ips.getIPlayer();
Justin Klaassen10d07c82017-09-15 17:58:39 -0400346 }
347
348 /**
349 * @hide
350 * Handle a change of audio attributes
351 * @param attr
352 */
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 */
365 public boolean handleStateEvent(int event) {
Justin Klaassenbc81c7a2017-09-18 17:38:50 -0400366 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 }
Justin Klaassen10d07c82017-09-15 17:58:39 -0400374 }
375 return changed;
376 }
377
378 // To report IPlayer death from death recipient
379 /** @hide */
380 public interface PlayerDeathMonitor {
381 public void playerDeath(int piid);
382 }
383 /** @hide */
384 public static PlayerDeathMonitor sPlayerDeathMonitor;
385
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 */
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 */
418 public void dump(PrintWriter pw) {
419 pw.println(" " + toLogFriendlyString(this));
420 }
421
422 /**
423 * @hide
424 */
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
449 public int hashCode() {
450 return Objects.hash(mPlayerIId, mPlayerType, mClientUid, mClientPid);
451 }
452
453 @Override
454 public int describeContents() {
455 return 0;
456 }
457
458 @Override
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);
Justin Klaassenbc81c7a2017-09-18 17:38:50 -0400466 final IPlayerShell ips;
467 synchronized (this) {
468 ips = mIPlayerShell;
469 }
470 dest.writeStrongInterface(ips == null ? null : ips.getIPlayer());
Justin Klaassen10d07c82017-09-15 17:58:39 -0400471 }
472
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
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
Justin Klaassenbc81c7a2017-09-18 17:38:50 -0400502 private volatile IPlayer mIPlayer;
Justin Klaassen10d07c82017-09-15 17:58:39 -0400503
504 IPlayerShell(@NonNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer) {
505 mMonitor = monitor;
506 mIPlayer = iplayer;
507 }
508
Justin Klaassenbc81c7a2017-09-18 17:38:50 -0400509 synchronized void monitorDeath() {
510 if (mIPlayer == null) {
511 return;
512 }
Justin Klaassen10d07c82017-09-15 17:58:39 -0400513 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
524 IPlayer getIPlayer() {
525 return mIPlayer;
526 }
527
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
Justin Klaassenbc81c7a2017-09-18 17:38:50 -0400535 synchronized void release() {
536 if (mIPlayer == null) {
537 return;
538 }
Justin Klaassen10d07c82017-09-15 17:58:39 -0400539 mIPlayer.asBinder().unlinkToDeath(this, 0);
Justin Klaassenbc81c7a2017-09-18 17:38:50 -0400540 mIPlayer = null;
541 Binder.flushPendingCommands();
Justin Klaassen10d07c82017-09-15 17:58:39 -0400542 }
543 }
544
545 //=====================================================================
546 // Utilities
547
548 /** @hide */
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:
Justin Klaassenbc81c7a2017-09-18 17:38:50 -0400563 return "unknown player type " + type + " - FIXME";
Justin Klaassen10d07c82017-09-15 17:58:39 -0400564 }
565 }
566
567 /** @hide */
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}