blob: ee8f1b3eec7728f4c51cb0c4aef0780cbe9ccdb3 [file] [log] [blame]
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -07001/*
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
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070019import android.annotation.NonNull;
Andy Hung035d4ec2017-01-24 13:45:02 -080020import android.annotation.Nullable;
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070021import android.app.ActivityThread;
22import android.app.AppOpsManager;
23import android.content.Context;
24import android.os.IBinder;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080025import android.os.Parcel;
26import android.os.Parcelable;
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070027import android.os.Process;
28import android.os.RemoteException;
29import android.os.ServiceManager;
30import android.util.Log;
31
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -070032import com.android.internal.annotations.GuardedBy;
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070033import com.android.internal.app.IAppOpsCallback;
34import com.android.internal.app.IAppOpsService;
35
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -070036import java.lang.ref.WeakReference;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080037import java.util.Objects;
38
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070039/**
40 * Class to encapsulate a number of common player operations:
41 * - AppOps for OP_PLAY_AUDIO
42 * - more to come (routing, transport control)
43 * @hide
44 */
45public abstract class PlayerBase {
46
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -070047 private static final String TAG = "PlayerBase";
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -070048 /** Debug app ops */
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -070049 private static final boolean DEBUG_APP_OPS = false;
Jean-Michel Trivib8748c62018-03-23 10:34:18 -070050 private static final boolean DEBUG = DEBUG_APP_OPS || false;
51 private static IAudioService sService; //lazy initialization, use getService()
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -070052
Jean-Michel Trivifcf1e542019-02-25 12:20:35 -080053 /** if true, only use OP_PLAY_AUDIO monitoring for logging, and rely on muting to happen
54 * in AudioFlinger */
55 private static final boolean USE_AUDIOFLINGER_MUTING_FOR_OP = true;
56
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070057 // parameters of the player that affect AppOps
58 protected AudioAttributes mAttributes;
Jean-Michel Trivi8ba7d402019-03-05 09:51:40 -080059
60 // volumes of the subclass "player volumes", as seen by the client of the subclass
61 // (e.g. what was passed in AudioTrack.setVolume(float)). The actual volume applied is
62 // the combination of the player volume, and the PlayerBase pan and volume multipliers
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070063 protected float mLeftVolume = 1.0f;
64 protected float mRightVolume = 1.0f;
65 protected float mAuxEffectSendLevel = 0.0f;
66
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -070067 // NEVER call into AudioService (see getService()) with mLock held: PlayerBase can run in
68 // the same process as AudioService, which can synchronously call back into this class,
69 // causing deadlocks between the two
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -080070 private final Object mLock = new Object();
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070071
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -070072 // for AppOps
73 private @Nullable IAppOpsService mAppOps;
Jean-Michel Trivifcf1e542019-02-25 12:20:35 -080074 private @Nullable IAppOpsCallback mAppOpsCallback;
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -070075 @GuardedBy("mLock")
76 private boolean mHasAppOpsPlayAudio = true;
77
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080078 private final int mImplType;
79 // uniquely identifies the Player Interface throughout the system (P I Id)
Jean-Michel Trivifcf1e542019-02-25 12:20:35 -080080 private int mPlayerIId = AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080081
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -070082 @GuardedBy("mLock")
83 private int mState;
84 @GuardedBy("mLock")
85 private int mStartDelayMs = 0;
86 @GuardedBy("mLock")
87 private float mPanMultiplierL = 1.0f;
88 @GuardedBy("mLock")
89 private float mPanMultiplierR = 1.0f;
Jean-Michel Trivi8ba7d402019-03-05 09:51:40 -080090 @GuardedBy("mLock")
91 private float mVolMultiplier = 1.0f;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080092
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070093 /**
94 * Constructor. Must be given audio attributes, as they are required for AppOps.
95 * @param attr non-null audio attributes
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080096 * @param class non-null class of the implementation of this abstract class
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070097 */
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080098 PlayerBase(@NonNull AudioAttributes attr, int implType) {
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070099 if (attr == null) {
100 throw new IllegalArgumentException("Illegal null AudioAttributes");
101 }
102 mAttributes = attr;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800103 mImplType = implType;
Jean-Michel Trivie9630032017-01-21 15:53:11 -0800104 mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE;
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800105 };
106
107 /**
108 * Call from derived class when instantiation / initialization is successful
109 */
110 protected void baseRegisterPlayer() {
Jean-Michel Trivifcf1e542019-02-25 12:20:35 -0800111 if (!USE_AUDIOFLINGER_MUTING_FOR_OP) {
112 IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
113 mAppOps = IAppOpsService.Stub.asInterface(b);
114 // initialize mHasAppOpsPlayAudio
115 updateAppOpsPlayAudio();
116 // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed
117 mAppOpsCallback = new IAppOpsCallbackWrapper(this);
118 try {
119 mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,
120 ActivityThread.currentPackageName(), mAppOpsCallback);
121 } catch (RemoteException e) {
122 Log.e(TAG, "Error registering appOps callback", e);
123 mHasAppOpsPlayAudio = false;
124 }
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700125 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800126 try {
Jean-Michel Trivifcf1e542019-02-25 12:20:35 -0800127 mPlayerIId = getService().trackPlayer(
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700128 new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800129 } catch (RemoteException e) {
130 Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
131 }
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700132 }
133
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700134 /**
135 * To be called whenever the audio attributes of the player change
136 * @param attr non-null audio attributes
137 */
138 void baseUpdateAudioAttributes(@NonNull AudioAttributes attr) {
139 if (attr == null) {
140 throw new IllegalArgumentException("Illegal null AudioAttributes");
141 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800142 try {
143 getService().playerAttributes(mPlayerIId, attr);
144 } catch (RemoteException e) {
145 Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e);
146 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800147 synchronized (mLock) {
Eric Laurente5a351c2017-09-27 20:11:51 -0700148 boolean attributesChanged = (mAttributes != attr);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700149 mAttributes = attr;
Eric Laurente5a351c2017-09-27 20:11:51 -0700150 updateAppOpsPlayAudio_sync(attributesChanged);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700151 }
152 }
153
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700154 private void updateState(int state) {
155 final int piid;
156 synchronized (mLock) {
157 mState = state;
158 piid = mPlayerIId;
159 }
160 try {
161 getService().playerEvent(piid, state);
162 } catch (RemoteException e) {
163 Log.e(TAG, "Error talking to audio service, "
164 + AudioPlaybackConfiguration.toLogFriendlyPlayerState(state)
165 + " state will not be tracked for piid=" + piid, e);
166 }
167 }
168
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700169 void baseStart() {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800170 if (DEBUG) { Log.v(TAG, "baseStart() piid=" + mPlayerIId); }
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700171 updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800172 synchronized (mLock) {
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700173 if (isRestricted_sync()) {
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700174 playerSetVolume(true/*muting*/,0, 0);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700175 }
176 }
177 }
178
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800179 void baseSetStartDelayMs(int delayMs) {
180 synchronized(mLock) {
181 mStartDelayMs = Math.max(delayMs, 0);
182 }
183 }
184
185 protected int getStartDelayMs() {
186 synchronized(mLock) {
187 return mStartDelayMs;
188 }
189 }
190
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800191 void basePause() {
192 if (DEBUG) { Log.v(TAG, "basePause() piid=" + mPlayerIId); }
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700193 updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED);
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800194 }
195
196 void baseStop() {
197 if (DEBUG) { Log.v(TAG, "baseStop() piid=" + mPlayerIId); }
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700198 updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED);
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800199 }
200
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800201 void baseSetPan(float pan) {
202 final float p = Math.min(Math.max(-1.0f, pan), 1.0f);
203 synchronized (mLock) {
204 if (p >= 0.0f) {
205 mPanMultiplierL = 1.0f - p;
206 mPanMultiplierR = 1.0f;
207 } else {
208 mPanMultiplierL = 1.0f;
209 mPanMultiplierR = 1.0f + p;
210 }
211 }
Jean-Michel Trivi8ba7d402019-03-05 09:51:40 -0800212 updatePlayerVolume();
213 }
214
215 private void updatePlayerVolume() {
216 final float finalLeftVol, finalRightVol;
217 final boolean isRestricted;
218 synchronized (mLock) {
219 finalLeftVol = mVolMultiplier * mLeftVolume * mPanMultiplierL;
220 finalRightVol = mVolMultiplier * mRightVolume * mPanMultiplierR;
221 isRestricted = isRestricted_sync();
222 }
223 playerSetVolume(isRestricted /*muting*/, finalLeftVol, finalRightVol);
224 }
225
226 void setVolumeMultiplier(float vol) {
227 synchronized (mLock) {
228 this.mVolMultiplier = vol;
229 }
230 updatePlayerVolume();
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800231 }
232
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700233 void baseSetVolume(float leftVolume, float rightVolume) {
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800234 synchronized (mLock) {
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700235 mLeftVolume = leftVolume;
236 mRightVolume = rightVolume;
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700237 }
Jean-Michel Trivi8ba7d402019-03-05 09:51:40 -0800238 updatePlayerVolume();
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700239 }
240
241 int baseSetAuxEffectSendLevel(float level) {
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800242 synchronized (mLock) {
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700243 mAuxEffectSendLevel = level;
244 if (isRestricted_sync()) {
245 return AudioSystem.SUCCESS;
246 }
247 }
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700248 return playerSetAuxEffectSendLevel(false/*muting*/, level);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700249 }
250
251 /**
252 * To be called from a subclass release or finalize method.
253 * Releases AppOps related resources.
254 */
255 void baseRelease() {
Jean-Michel Trivie9630032017-01-21 15:53:11 -0800256 if (DEBUG) { Log.v(TAG, "baseRelease() piid=" + mPlayerIId + " state=" + mState); }
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700257 boolean releasePlayer = false;
258 synchronized (mLock) {
259 if (mState != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) {
260 releasePlayer = true;
261 mState = AudioPlaybackConfiguration.PLAYER_STATE_RELEASED;
262 }
263 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800264 try {
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700265 if (releasePlayer) {
266 getService().releasePlayer(mPlayerIId);
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800267 }
268 } catch (RemoteException e) {
269 Log.e(TAG, "Error talking to audio service, the player will still be tracked", e);
270 }
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700271 try {
Jean-Michel Trivi0fbcb862017-02-23 10:21:17 -0800272 if (mAppOps != null) {
273 mAppOps.stopWatchingMode(mAppOpsCallback);
274 }
Jean-Michel Trivia00f8602017-03-22 08:39:42 -0700275 } catch (Exception e) {
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700276 // nothing to do here, the object is supposed to be released anyway
277 }
278 }
279
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -0700280 private void updateAppOpsPlayAudio() {
281 synchronized (mLock) {
Eric Laurente5a351c2017-09-27 20:11:51 -0700282 updateAppOpsPlayAudio_sync(false);
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -0700283 }
284 }
285
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700286 /**
287 * To be called whenever a condition that might affect audibility of this player is updated.
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800288 * Must be called synchronized on mLock.
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700289 */
Eric Laurente5a351c2017-09-27 20:11:51 -0700290 void updateAppOpsPlayAudio_sync(boolean attributesChanged) {
Jean-Michel Trivifcf1e542019-02-25 12:20:35 -0800291 if (USE_AUDIOFLINGER_MUTING_FOR_OP) {
292 return;
293 }
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700294 boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio;
295 try {
Jean-Michel Trivi0fbcb862017-02-23 10:21:17 -0800296 int mode = AppOpsManager.MODE_IGNORED;
297 if (mAppOps != null) {
298 mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700299 mAttributes.getUsage(),
300 Process.myUid(), ActivityThread.currentPackageName());
Jean-Michel Trivi0fbcb862017-02-23 10:21:17 -0800301 }
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700302 mHasAppOpsPlayAudio = (mode == AppOpsManager.MODE_ALLOWED);
303 } catch (RemoteException e) {
304 mHasAppOpsPlayAudio = false;
305 }
306
307 // AppsOps alters a player's volume; when the restriction changes, reflect it on the actual
308 // volume used by the player
309 try {
Eric Laurente5a351c2017-09-27 20:11:51 -0700310 if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio ||
311 attributesChanged) {
Jean-Michel Trivi3120059d2017-08-28 12:40:55 -0700312 getService().playerHasOpPlayAudio(mPlayerIId, mHasAppOpsPlayAudio);
Eric Laurente5a351c2017-09-27 20:11:51 -0700313 if (!isRestricted_sync()) {
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700314 if (DEBUG_APP_OPS) {
315 Log.v(TAG, "updateAppOpsPlayAudio: unmuting player, vol=" + mLeftVolume
316 + "/" + mRightVolume);
317 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800318 playerSetVolume(false/*muting*/,
319 mLeftVolume * mPanMultiplierL, mRightVolume * mPanMultiplierR);
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700320 playerSetAuxEffectSendLevel(false/*muting*/, mAuxEffectSendLevel);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700321 } else {
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700322 if (DEBUG_APP_OPS) {
323 Log.v(TAG, "updateAppOpsPlayAudio: muting player");
324 }
325 playerSetVolume(true/*muting*/, 0.0f, 0.0f);
326 playerSetAuxEffectSendLevel(true/*muting*/, 0.0f);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700327 }
328 }
329 } catch (Exception e) {
330 // failing silently, player might not be in right state
331 }
332 }
333
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700334 /**
335 * To be called by the subclass whenever an operation is potentially restricted.
336 * As the media player-common behavior are incorporated into this class, the subclass's need
337 * to call this method should be removed, and this method could become private.
338 * FIXME can this method be private so subclasses don't have to worry about when to check
339 * the restrictions.
340 * @return
341 */
342 boolean isRestricted_sync() {
Jean-Michel Trivifcf1e542019-02-25 12:20:35 -0800343 if (USE_AUDIOFLINGER_MUTING_FOR_OP) {
344 return false;
345 }
Jean-Michel Trivi1293c062016-07-13 11:25:21 -0700346 // check app ops
347 if (mHasAppOpsPlayAudio) {
348 return false;
349 }
350 // check bypass flag
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700351 if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
352 return false;
353 }
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700354 // check force audibility flag and camera restriction
355 if (((mAttributes.getAllFlags() & AudioAttributes.FLAG_AUDIBILITY_ENFORCED) != 0)
356 && (mAttributes.getUsage() == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)) {
357 boolean cameraSoundForced = false;
358 try {
359 cameraSoundForced = getService().isCameraSoundForced();
360 } catch (RemoteException e) {
361 Log.e(TAG, "Cannot access AudioService in isRestricted_sync()");
362 } catch (NullPointerException e) {
363 Log.e(TAG, "Null AudioService in isRestricted_sync()");
364 }
365 if (cameraSoundForced) {
366 return false;
367 }
368 }
Jean-Michel Trivi1293c062016-07-13 11:25:21 -0700369 return true;
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700370 }
371
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700372 private static IAudioService getService()
373 {
374 if (sService != null) {
375 return sService;
376 }
377 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
378 sService = IAudioService.Stub.asInterface(b);
379 return sService;
380 }
381
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800382 /**
383 * @hide
384 * @param delayMs
385 */
386 public void setStartDelayMs(int delayMs) {
387 baseSetStartDelayMs(delayMs);
388 }
389
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800390 //=====================================================================
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700391 // Abstract methods a subclass needs to implement
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700392 /**
393 * Abstract method for the subclass behavior's for volume and muting commands
394 * @param muting if true, the player is to be muted, and the volume values can be ignored
395 * @param leftVolume the left volume to use if muting is false
396 * @param rightVolume the right volume to use if muting is false
397 */
398 abstract void playerSetVolume(boolean muting, float leftVolume, float rightVolume);
Andy Hung035d4ec2017-01-24 13:45:02 -0800399
400 /**
401 * Abstract method to apply a {@link VolumeShaper.Configuration}
402 * and a {@link VolumeShaper.Operation} to the Player.
403 * This should be overridden by the Player to call into the native
404 * VolumeShaper implementation. Multiple {@code VolumeShapers} may be
405 * concurrently active for a given Player, each accessible by the
406 * {@code VolumeShaper} id.
407 *
408 * The {@code VolumeShaper} implementation caches the id returned
409 * when applying a fully specified configuration
410 * from {VolumeShaper.Configuration.Builder} to track later
411 * operation changes requested on it.
412 *
413 * @param configuration a {@code VolumeShaper.Configuration} object
414 * created by {@link VolumeShaper.Configuration.Builder} or
415 * an created from a {@code VolumeShaper} id
416 * by the {@link VolumeShaper.Configuration} constructor.
417 * @param operation a {@code VolumeShaper.Operation}.
418 * @return a negative error status or a
419 * non-negative {@code VolumeShaper} id on success.
420 */
421 /* package */ abstract int playerApplyVolumeShaper(
422 @NonNull VolumeShaper.Configuration configuration,
423 @NonNull VolumeShaper.Operation operation);
424
425 /**
426 * Abstract method to get the current VolumeShaper state.
427 * @param id the {@code VolumeShaper} id returned from
428 * sending a fully specified {@code VolumeShaper.Configuration}
429 * through {@link #playerApplyVolumeShaper}
430 * @return a {@code VolumeShaper.State} object or null if
431 * there is no {@code VolumeShaper} for the id.
432 */
433 /* package */ abstract @Nullable VolumeShaper.State playerGetVolumeShaperState(int id);
434
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700435 abstract int playerSetAuxEffectSendLevel(boolean muting, float level);
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800436 abstract void playerStart();
437 abstract void playerPause();
438 abstract void playerStop();
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -0800439
440 //=====================================================================
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -0700441 private static class IAppOpsCallbackWrapper extends IAppOpsCallback.Stub {
442 private final WeakReference<PlayerBase> mWeakPB;
443
444 public IAppOpsCallbackWrapper(PlayerBase pb) {
445 mWeakPB = new WeakReference<PlayerBase>(pb);
446 }
447
448 @Override
449 public void opChanged(int op, int uid, String packageName) {
450 if (op == AppOpsManager.OP_PLAY_AUDIO) {
451 if (DEBUG_APP_OPS) { Log.v(TAG, "opChanged: op=PLAY_AUDIO pack=" + packageName); }
452 final PlayerBase pb = mWeakPB.get();
453 if (pb != null) {
454 pb.updateAppOpsPlayAudio();
455 }
456 }
457 }
458 }
459
460 //=====================================================================
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800461 /**
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700462 * Wrapper around an implementation of IPlayer for all subclasses of PlayerBase
463 * that doesn't keep a strong reference on PlayerBase
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800464 */
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700465 private static class IPlayerWrapper extends IPlayer.Stub {
466 private final WeakReference<PlayerBase> mWeakPB;
467
468 public IPlayerWrapper(PlayerBase pb) {
469 mWeakPB = new WeakReference<PlayerBase>(pb);
470 }
471
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800472 @Override
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800473 public void start() {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700474 final PlayerBase pb = mWeakPB.get();
475 if (pb != null) {
476 pb.playerStart();
477 }
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800478 }
479
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800480 @Override
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800481 public void pause() {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700482 final PlayerBase pb = mWeakPB.get();
483 if (pb != null) {
484 pb.playerPause();
485 }
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800486 }
487
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800488 @Override
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800489 public void stop() {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700490 final PlayerBase pb = mWeakPB.get();
491 if (pb != null) {
492 pb.playerStop();
493 }
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800494 }
495
496 @Override
497 public void setVolume(float vol) {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700498 final PlayerBase pb = mWeakPB.get();
499 if (pb != null) {
Jean-Michel Trivi8ba7d402019-03-05 09:51:40 -0800500 pb.setVolumeMultiplier(vol);
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700501 }
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800502 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800503
504 @Override
505 public void setPan(float pan) {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700506 final PlayerBase pb = mWeakPB.get();
507 if (pb != null) {
508 pb.baseSetPan(pan);
509 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800510 }
511
512 @Override
513 public void setStartDelayMs(int delayMs) {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700514 final PlayerBase pb = mWeakPB.get();
515 if (pb != null) {
516 pb.baseSetStartDelayMs(delayMs);
517 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800518 }
Andy Hung035d4ec2017-01-24 13:45:02 -0800519
520 @Override
521 public void applyVolumeShaper(
522 @NonNull VolumeShaper.Configuration configuration,
523 @NonNull VolumeShaper.Operation operation) {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700524 final PlayerBase pb = mWeakPB.get();
525 if (pb != null) {
526 pb.playerApplyVolumeShaper(configuration, operation);
527 }
Andy Hung035d4ec2017-01-24 13:45:02 -0800528 }
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700529 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800530
531 //=====================================================================
532 /**
533 * Class holding all the information about a player that needs to be known at registration time
534 */
535 public static class PlayerIdCard implements Parcelable {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800536 public final int mPlayerType;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800537
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -0700538 public static final int AUDIO_ATTRIBUTES_NONE = 0;
539 public static final int AUDIO_ATTRIBUTES_DEFINED = 1;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800540 public final AudioAttributes mAttributes;
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800541 public final IPlayer mIPlayer;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800542
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800543 PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800544 mPlayerType = type;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800545 mAttributes = attr;
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800546 mIPlayer = iplayer;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800547 }
548
549 @Override
550 public int hashCode() {
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -0800551 return Objects.hash(mPlayerType);
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800552 }
553
554 @Override
555 public int describeContents() {
556 return 0;
557 }
558
559 @Override
560 public void writeToParcel(Parcel dest, int flags) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800561 dest.writeInt(mPlayerType);
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800562 mAttributes.writeToParcel(dest, 0);
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800563 dest.writeStrongBinder(mIPlayer == null ? null : mIPlayer.asBinder());
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800564 }
565
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700566 public static final @android.annotation.NonNull Parcelable.Creator<PlayerIdCard> CREATOR
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800567 = new Parcelable.Creator<PlayerIdCard>() {
568 /**
569 * Rebuilds an PlayerIdCard previously stored with writeToParcel().
570 * @param p Parcel object to read the PlayerIdCard from
571 * @return a new PlayerIdCard created from the data in the parcel
572 */
573 public PlayerIdCard createFromParcel(Parcel p) {
574 return new PlayerIdCard(p);
575 }
576 public PlayerIdCard[] newArray(int size) {
577 return new PlayerIdCard[size];
578 }
579 };
580
581 private PlayerIdCard(Parcel in) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800582 mPlayerType = in.readInt();
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800583 mAttributes = AudioAttributes.CREATOR.createFromParcel(in);
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800584 // IPlayer can be null if unmarshalling a Parcel coming from who knows where
585 final IBinder b = in.readStrongBinder();
586 mIPlayer = (b == null ? null : IPlayer.Stub.asInterface(b));
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800587 }
588
589 @Override
590 public boolean equals(Object o) {
591 if (this == o) return true;
592 if (o == null || !(o instanceof PlayerIdCard)) return false;
593
594 PlayerIdCard that = (PlayerIdCard) o;
595
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -0800596 // FIXME change to the binder player interface once supported as a member
597 return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes));
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800598 }
599 }
600
601 //=====================================================================
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -0800602 // Utilities
603
604 /**
Jean-Michel Trivi2f52ac62019-03-04 09:08:47 -0800605 * @hide
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -0800606 * Use to generate warning or exception in legacy code paths that allowed passing stream types
607 * to qualify audio playback.
608 * @param streamType the stream type to check
609 * @throws IllegalArgumentException
610 */
Jean-Michel Trivi16f0a6a2019-03-02 12:24:56 -0800611 public static void deprecateStreamTypeForPlayback(int streamType, @NonNull String className,
612 @NonNull String opName) throws IllegalArgumentException {
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -0800613 // STREAM_ACCESSIBILITY was introduced at the same time the use of stream types
614 // for audio playback was deprecated, so it is not allowed at all to qualify a playback
615 // use case
616 if (streamType == AudioManager.STREAM_ACCESSIBILITY) {
617 throw new IllegalArgumentException("Use of STREAM_ACCESSIBILITY is reserved for "
618 + "volume control");
619 }
Jean-Michel Trivic4c13b42017-05-02 10:01:08 -0700620 Log.w(className, "Use of stream types is deprecated for operations other than " +
621 "volume control");
622 Log.w(className, "See the documentation of " + opName + " for what to use instead with " +
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -0800623 "android.media.AudioAttributes to qualify your playback use case");
624 }
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700625}