blob: 34d56ede8385ac67c3fcc6c93ccfb5ef750e87b9 [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;
Andy Hung035d4ec2017-01-24 13:45:02 -080024import android.media.VolumeShaper;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080025import android.os.Binder;
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070026import android.os.IBinder;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080027import android.os.Parcel;
28import android.os.Parcelable;
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070029import android.os.Process;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.util.Log;
33
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -070034import com.android.internal.annotations.GuardedBy;
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070035import com.android.internal.app.IAppOpsCallback;
36import com.android.internal.app.IAppOpsService;
37
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080038import java.lang.IllegalArgumentException;
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -070039import java.lang.ref.WeakReference;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080040import java.util.Objects;
41
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070042/**
43 * Class to encapsulate a number of common player operations:
44 * - AppOps for OP_PLAY_AUDIO
45 * - more to come (routing, transport control)
46 * @hide
47 */
48public abstract class PlayerBase {
49
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -070050 private static final String TAG = "PlayerBase";
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -070051 /** Debug app ops */
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -070052 private static final boolean DEBUG_APP_OPS = false;
Jean-Michel Trivib8748c62018-03-23 10:34:18 -070053 private static final boolean DEBUG = DEBUG_APP_OPS || false;
54 private static IAudioService sService; //lazy initialization, use getService()
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -070055
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070056 // parameters of the player that affect AppOps
57 protected AudioAttributes mAttributes;
58 protected float mLeftVolume = 1.0f;
59 protected float mRightVolume = 1.0f;
60 protected float mAuxEffectSendLevel = 0.0f;
61
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -070062 // NEVER call into AudioService (see getService()) with mLock held: PlayerBase can run in
63 // the same process as AudioService, which can synchronously call back into this class,
64 // causing deadlocks between the two
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -080065 private final Object mLock = new Object();
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070066
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -070067 // for AppOps
68 private @Nullable IAppOpsService mAppOps;
69 private IAppOpsCallback mAppOpsCallback;
70 @GuardedBy("mLock")
71 private boolean mHasAppOpsPlayAudio = true;
72
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080073 private final int mImplType;
74 // uniquely identifies the Player Interface throughout the system (P I Id)
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -070075 private int mPlayerIId = AudioPlaybackConfiguration.PLAYER_PIID_UNASSIGNED;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080076
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -070077 @GuardedBy("mLock")
78 private int mState;
79 @GuardedBy("mLock")
80 private int mStartDelayMs = 0;
81 @GuardedBy("mLock")
82 private float mPanMultiplierL = 1.0f;
83 @GuardedBy("mLock")
84 private float mPanMultiplierR = 1.0f;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080085
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070086 /**
87 * Constructor. Must be given audio attributes, as they are required for AppOps.
88 * @param attr non-null audio attributes
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080089 * @param class non-null class of the implementation of this abstract class
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070090 */
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080091 PlayerBase(@NonNull AudioAttributes attr, int implType) {
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -070092 if (attr == null) {
93 throw new IllegalArgumentException("Illegal null AudioAttributes");
94 }
95 mAttributes = attr;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080096 mImplType = implType;
Jean-Michel Trivie9630032017-01-21 15:53:11 -080097 mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE;
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -080098 };
99
100 /**
101 * Call from derived class when instantiation / initialization is successful
102 */
103 protected void baseRegisterPlayer() {
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -0800104 int newPiid = AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700105 IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
106 mAppOps = IAppOpsService.Stub.asInterface(b);
107 // initialize mHasAppOpsPlayAudio
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -0700108 updateAppOpsPlayAudio();
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700109 // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -0700110 mAppOpsCallback = new IAppOpsCallbackWrapper(this);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700111 try {
112 mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,
113 ActivityThread.currentPackageName(), mAppOpsCallback);
114 } catch (RemoteException e) {
Jean-Michel Trivib8748c62018-03-23 10:34:18 -0700115 Log.e(TAG, "Error registering appOps callback", e);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700116 mHasAppOpsPlayAudio = false;
117 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800118 try {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700119 newPiid = getService().trackPlayer(
120 new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800121 } catch (RemoteException e) {
122 Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
123 }
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -0800124 mPlayerIId = newPiid;
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700125 }
126
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700127 /**
128 * To be called whenever the audio attributes of the player change
129 * @param attr non-null audio attributes
130 */
131 void baseUpdateAudioAttributes(@NonNull AudioAttributes attr) {
132 if (attr == null) {
133 throw new IllegalArgumentException("Illegal null AudioAttributes");
134 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800135 try {
136 getService().playerAttributes(mPlayerIId, attr);
137 } catch (RemoteException e) {
138 Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e);
139 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800140 synchronized (mLock) {
Eric Laurente5a351c2017-09-27 20:11:51 -0700141 boolean attributesChanged = (mAttributes != attr);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700142 mAttributes = attr;
Eric Laurente5a351c2017-09-27 20:11:51 -0700143 updateAppOpsPlayAudio_sync(attributesChanged);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700144 }
145 }
146
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700147 private void updateState(int state) {
148 final int piid;
149 synchronized (mLock) {
150 mState = state;
151 piid = mPlayerIId;
152 }
153 try {
154 getService().playerEvent(piid, state);
155 } catch (RemoteException e) {
156 Log.e(TAG, "Error talking to audio service, "
157 + AudioPlaybackConfiguration.toLogFriendlyPlayerState(state)
158 + " state will not be tracked for piid=" + piid, e);
159 }
160 }
161
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700162 void baseStart() {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800163 if (DEBUG) { Log.v(TAG, "baseStart() piid=" + mPlayerIId); }
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700164 updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800165 synchronized (mLock) {
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700166 if (isRestricted_sync()) {
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700167 playerSetVolume(true/*muting*/,0, 0);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700168 }
169 }
170 }
171
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800172 void baseSetStartDelayMs(int delayMs) {
173 synchronized(mLock) {
174 mStartDelayMs = Math.max(delayMs, 0);
175 }
176 }
177
178 protected int getStartDelayMs() {
179 synchronized(mLock) {
180 return mStartDelayMs;
181 }
182 }
183
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800184 void basePause() {
185 if (DEBUG) { Log.v(TAG, "basePause() piid=" + mPlayerIId); }
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700186 updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED);
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800187 }
188
189 void baseStop() {
190 if (DEBUG) { Log.v(TAG, "baseStop() piid=" + mPlayerIId); }
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700191 updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED);
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800192 }
193
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800194 void baseSetPan(float pan) {
195 final float p = Math.min(Math.max(-1.0f, pan), 1.0f);
196 synchronized (mLock) {
197 if (p >= 0.0f) {
198 mPanMultiplierL = 1.0f - p;
199 mPanMultiplierR = 1.0f;
200 } else {
201 mPanMultiplierL = 1.0f;
202 mPanMultiplierR = 1.0f + p;
203 }
204 }
205 baseSetVolume(mLeftVolume, mRightVolume);
206 }
207
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700208 void baseSetVolume(float leftVolume, float rightVolume) {
Eric Laurente5a351c2017-09-27 20:11:51 -0700209 final boolean isRestricted;
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800210 synchronized (mLock) {
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700211 mLeftVolume = leftVolume;
212 mRightVolume = rightVolume;
Eric Laurente5a351c2017-09-27 20:11:51 -0700213 isRestricted = isRestricted_sync();
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700214 }
Eric Laurente5a351c2017-09-27 20:11:51 -0700215 playerSetVolume(isRestricted/*muting*/,
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800216 leftVolume * mPanMultiplierL, rightVolume * mPanMultiplierR);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700217 }
218
219 int baseSetAuxEffectSendLevel(float level) {
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800220 synchronized (mLock) {
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700221 mAuxEffectSendLevel = level;
222 if (isRestricted_sync()) {
223 return AudioSystem.SUCCESS;
224 }
225 }
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700226 return playerSetAuxEffectSendLevel(false/*muting*/, level);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700227 }
228
229 /**
230 * To be called from a subclass release or finalize method.
231 * Releases AppOps related resources.
232 */
233 void baseRelease() {
Jean-Michel Trivie9630032017-01-21 15:53:11 -0800234 if (DEBUG) { Log.v(TAG, "baseRelease() piid=" + mPlayerIId + " state=" + mState); }
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700235 boolean releasePlayer = false;
236 synchronized (mLock) {
237 if (mState != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) {
238 releasePlayer = true;
239 mState = AudioPlaybackConfiguration.PLAYER_STATE_RELEASED;
240 }
241 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800242 try {
Jean-Michel Trivi76e124b2018-04-10 14:06:07 -0700243 if (releasePlayer) {
244 getService().releasePlayer(mPlayerIId);
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800245 }
246 } catch (RemoteException e) {
247 Log.e(TAG, "Error talking to audio service, the player will still be tracked", e);
248 }
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700249 try {
Jean-Michel Trivi0fbcb862017-02-23 10:21:17 -0800250 if (mAppOps != null) {
251 mAppOps.stopWatchingMode(mAppOpsCallback);
252 }
Jean-Michel Trivia00f8602017-03-22 08:39:42 -0700253 } catch (Exception e) {
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700254 // nothing to do here, the object is supposed to be released anyway
255 }
256 }
257
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -0700258 private void updateAppOpsPlayAudio() {
259 synchronized (mLock) {
Eric Laurente5a351c2017-09-27 20:11:51 -0700260 updateAppOpsPlayAudio_sync(false);
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -0700261 }
262 }
263
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700264 /**
265 * To be called whenever a condition that might affect audibility of this player is updated.
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800266 * Must be called synchronized on mLock.
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700267 */
Eric Laurente5a351c2017-09-27 20:11:51 -0700268 void updateAppOpsPlayAudio_sync(boolean attributesChanged) {
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700269 boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio;
270 try {
Jean-Michel Trivi0fbcb862017-02-23 10:21:17 -0800271 int mode = AppOpsManager.MODE_IGNORED;
272 if (mAppOps != null) {
273 mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700274 mAttributes.getUsage(),
275 Process.myUid(), ActivityThread.currentPackageName());
Jean-Michel Trivi0fbcb862017-02-23 10:21:17 -0800276 }
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700277 mHasAppOpsPlayAudio = (mode == AppOpsManager.MODE_ALLOWED);
278 } catch (RemoteException e) {
279 mHasAppOpsPlayAudio = false;
280 }
281
282 // AppsOps alters a player's volume; when the restriction changes, reflect it on the actual
283 // volume used by the player
284 try {
Eric Laurente5a351c2017-09-27 20:11:51 -0700285 if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio ||
286 attributesChanged) {
Jean-Michel Trivi3120059d2017-08-28 12:40:55 -0700287 getService().playerHasOpPlayAudio(mPlayerIId, mHasAppOpsPlayAudio);
Eric Laurente5a351c2017-09-27 20:11:51 -0700288 if (!isRestricted_sync()) {
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700289 if (DEBUG_APP_OPS) {
290 Log.v(TAG, "updateAppOpsPlayAudio: unmuting player, vol=" + mLeftVolume
291 + "/" + mRightVolume);
292 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800293 playerSetVolume(false/*muting*/,
294 mLeftVolume * mPanMultiplierL, mRightVolume * mPanMultiplierR);
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700295 playerSetAuxEffectSendLevel(false/*muting*/, mAuxEffectSendLevel);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700296 } else {
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700297 if (DEBUG_APP_OPS) {
298 Log.v(TAG, "updateAppOpsPlayAudio: muting player");
299 }
300 playerSetVolume(true/*muting*/, 0.0f, 0.0f);
301 playerSetAuxEffectSendLevel(true/*muting*/, 0.0f);
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700302 }
303 }
304 } catch (Exception e) {
305 // failing silently, player might not be in right state
306 }
307 }
308
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700309 /**
310 * To be called by the subclass whenever an operation is potentially restricted.
311 * As the media player-common behavior are incorporated into this class, the subclass's need
312 * to call this method should be removed, and this method could become private.
313 * FIXME can this method be private so subclasses don't have to worry about when to check
314 * the restrictions.
315 * @return
316 */
317 boolean isRestricted_sync() {
Jean-Michel Trivi1293c062016-07-13 11:25:21 -0700318 // check app ops
319 if (mHasAppOpsPlayAudio) {
320 return false;
321 }
322 // check bypass flag
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700323 if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
324 return false;
325 }
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700326 // check force audibility flag and camera restriction
327 if (((mAttributes.getAllFlags() & AudioAttributes.FLAG_AUDIBILITY_ENFORCED) != 0)
328 && (mAttributes.getUsage() == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)) {
329 boolean cameraSoundForced = false;
330 try {
331 cameraSoundForced = getService().isCameraSoundForced();
332 } catch (RemoteException e) {
333 Log.e(TAG, "Cannot access AudioService in isRestricted_sync()");
334 } catch (NullPointerException e) {
335 Log.e(TAG, "Null AudioService in isRestricted_sync()");
336 }
337 if (cameraSoundForced) {
338 return false;
339 }
340 }
Jean-Michel Trivi1293c062016-07-13 11:25:21 -0700341 return true;
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700342 }
343
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700344 private static IAudioService getService()
345 {
346 if (sService != null) {
347 return sService;
348 }
349 IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
350 sService = IAudioService.Stub.asInterface(b);
351 return sService;
352 }
353
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800354 /**
355 * @hide
356 * @param delayMs
357 */
358 public void setStartDelayMs(int delayMs) {
359 baseSetStartDelayMs(delayMs);
360 }
361
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800362 //=====================================================================
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700363 // Abstract methods a subclass needs to implement
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700364 /**
365 * Abstract method for the subclass behavior's for volume and muting commands
366 * @param muting if true, the player is to be muted, and the volume values can be ignored
367 * @param leftVolume the left volume to use if muting is false
368 * @param rightVolume the right volume to use if muting is false
369 */
370 abstract void playerSetVolume(boolean muting, float leftVolume, float rightVolume);
Andy Hung035d4ec2017-01-24 13:45:02 -0800371
372 /**
373 * Abstract method to apply a {@link VolumeShaper.Configuration}
374 * and a {@link VolumeShaper.Operation} to the Player.
375 * This should be overridden by the Player to call into the native
376 * VolumeShaper implementation. Multiple {@code VolumeShapers} may be
377 * concurrently active for a given Player, each accessible by the
378 * {@code VolumeShaper} id.
379 *
380 * The {@code VolumeShaper} implementation caches the id returned
381 * when applying a fully specified configuration
382 * from {VolumeShaper.Configuration.Builder} to track later
383 * operation changes requested on it.
384 *
385 * @param configuration a {@code VolumeShaper.Configuration} object
386 * created by {@link VolumeShaper.Configuration.Builder} or
387 * an created from a {@code VolumeShaper} id
388 * by the {@link VolumeShaper.Configuration} constructor.
389 * @param operation a {@code VolumeShaper.Operation}.
390 * @return a negative error status or a
391 * non-negative {@code VolumeShaper} id on success.
392 */
393 /* package */ abstract int playerApplyVolumeShaper(
394 @NonNull VolumeShaper.Configuration configuration,
395 @NonNull VolumeShaper.Operation operation);
396
397 /**
398 * Abstract method to get the current VolumeShaper state.
399 * @param id the {@code VolumeShaper} id returned from
400 * sending a fully specified {@code VolumeShaper.Configuration}
401 * through {@link #playerApplyVolumeShaper}
402 * @return a {@code VolumeShaper.State} object or null if
403 * there is no {@code VolumeShaper} for the id.
404 */
405 /* package */ abstract @Nullable VolumeShaper.State playerGetVolumeShaperState(int id);
406
Jean-Michel Trivi8e48c692016-10-19 11:52:08 -0700407 abstract int playerSetAuxEffectSendLevel(boolean muting, float level);
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800408 abstract void playerStart();
409 abstract void playerPause();
410 abstract void playerStop();
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -0800411
412 //=====================================================================
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -0700413 private static class IAppOpsCallbackWrapper extends IAppOpsCallback.Stub {
414 private final WeakReference<PlayerBase> mWeakPB;
415
416 public IAppOpsCallbackWrapper(PlayerBase pb) {
417 mWeakPB = new WeakReference<PlayerBase>(pb);
418 }
419
420 @Override
421 public void opChanged(int op, int uid, String packageName) {
422 if (op == AppOpsManager.OP_PLAY_AUDIO) {
423 if (DEBUG_APP_OPS) { Log.v(TAG, "opChanged: op=PLAY_AUDIO pack=" + packageName); }
424 final PlayerBase pb = mWeakPB.get();
425 if (pb != null) {
426 pb.updateAppOpsPlayAudio();
427 }
428 }
429 }
430 }
431
432 //=====================================================================
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800433 /**
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700434 * Wrapper around an implementation of IPlayer for all subclasses of PlayerBase
435 * that doesn't keep a strong reference on PlayerBase
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800436 */
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700437 private static class IPlayerWrapper extends IPlayer.Stub {
438 private final WeakReference<PlayerBase> mWeakPB;
439
440 public IPlayerWrapper(PlayerBase pb) {
441 mWeakPB = new WeakReference<PlayerBase>(pb);
442 }
443
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800444 @Override
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800445 public void start() {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700446 final PlayerBase pb = mWeakPB.get();
447 if (pb != null) {
448 pb.playerStart();
449 }
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800450 }
451
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800452 @Override
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800453 public void pause() {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700454 final PlayerBase pb = mWeakPB.get();
455 if (pb != null) {
456 pb.playerPause();
457 }
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800458 }
459
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800460 @Override
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800461 public void stop() {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700462 final PlayerBase pb = mWeakPB.get();
463 if (pb != null) {
464 pb.playerStop();
465 }
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800466 }
467
468 @Override
469 public void setVolume(float vol) {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700470 final PlayerBase pb = mWeakPB.get();
471 if (pb != null) {
472 pb.baseSetVolume(vol, vol);
473 }
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800474 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800475
476 @Override
477 public void setPan(float pan) {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700478 final PlayerBase pb = mWeakPB.get();
479 if (pb != null) {
480 pb.baseSetPan(pan);
481 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800482 }
483
484 @Override
485 public void setStartDelayMs(int delayMs) {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700486 final PlayerBase pb = mWeakPB.get();
487 if (pb != null) {
488 pb.baseSetStartDelayMs(delayMs);
489 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800490 }
Andy Hung035d4ec2017-01-24 13:45:02 -0800491
492 @Override
493 public void applyVolumeShaper(
494 @NonNull VolumeShaper.Configuration configuration,
495 @NonNull VolumeShaper.Operation operation) {
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700496 final PlayerBase pb = mWeakPB.get();
497 if (pb != null) {
498 pb.playerApplyVolumeShaper(configuration, operation);
499 }
Andy Hung035d4ec2017-01-24 13:45:02 -0800500 }
Jean-Michel Trivicf1b2242017-05-09 11:19:42 -0700501 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800502
503 //=====================================================================
504 /**
505 * Class holding all the information about a player that needs to be known at registration time
506 */
507 public static class PlayerIdCard implements Parcelable {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800508 public final int mPlayerType;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800509
Jean-Michel Triviaee6ee92017-05-09 16:19:36 -0700510 public static final int AUDIO_ATTRIBUTES_NONE = 0;
511 public static final int AUDIO_ATTRIBUTES_DEFINED = 1;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800512 public final AudioAttributes mAttributes;
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800513 public final IPlayer mIPlayer;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800514
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800515 PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800516 mPlayerType = type;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800517 mAttributes = attr;
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800518 mIPlayer = iplayer;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800519 }
520
521 @Override
522 public int hashCode() {
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -0800523 return Objects.hash(mPlayerType);
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800524 }
525
526 @Override
527 public int describeContents() {
528 return 0;
529 }
530
531 @Override
532 public void writeToParcel(Parcel dest, int flags) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800533 dest.writeInt(mPlayerType);
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800534 mAttributes.writeToParcel(dest, 0);
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800535 dest.writeStrongBinder(mIPlayer == null ? null : mIPlayer.asBinder());
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800536 }
537
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700538 public static final @android.annotation.NonNull Parcelable.Creator<PlayerIdCard> CREATOR
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800539 = new Parcelable.Creator<PlayerIdCard>() {
540 /**
541 * Rebuilds an PlayerIdCard previously stored with writeToParcel().
542 * @param p Parcel object to read the PlayerIdCard from
543 * @return a new PlayerIdCard created from the data in the parcel
544 */
545 public PlayerIdCard createFromParcel(Parcel p) {
546 return new PlayerIdCard(p);
547 }
548 public PlayerIdCard[] newArray(int size) {
549 return new PlayerIdCard[size];
550 }
551 };
552
553 private PlayerIdCard(Parcel in) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800554 mPlayerType = in.readInt();
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800555 mAttributes = AudioAttributes.CREATOR.createFromParcel(in);
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800556 // IPlayer can be null if unmarshalling a Parcel coming from who knows where
557 final IBinder b = in.readStrongBinder();
558 mIPlayer = (b == null ? null : IPlayer.Stub.asInterface(b));
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800559 }
560
561 @Override
562 public boolean equals(Object o) {
563 if (this == o) return true;
564 if (o == null || !(o instanceof PlayerIdCard)) return false;
565
566 PlayerIdCard that = (PlayerIdCard) o;
567
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -0800568 // FIXME change to the binder player interface once supported as a member
569 return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes));
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800570 }
571 }
572
573 //=====================================================================
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -0800574 // Utilities
575
576 /**
577 * Use to generate warning or exception in legacy code paths that allowed passing stream types
578 * to qualify audio playback.
579 * @param streamType the stream type to check
580 * @throws IllegalArgumentException
581 */
582 public static void deprecateStreamTypeForPlayback(int streamType, String className,
583 String opName) throws IllegalArgumentException {
584 // STREAM_ACCESSIBILITY was introduced at the same time the use of stream types
585 // for audio playback was deprecated, so it is not allowed at all to qualify a playback
586 // use case
587 if (streamType == AudioManager.STREAM_ACCESSIBILITY) {
588 throw new IllegalArgumentException("Use of STREAM_ACCESSIBILITY is reserved for "
589 + "volume control");
590 }
Jean-Michel Trivic4c13b42017-05-02 10:01:08 -0700591 Log.w(className, "Use of stream types is deprecated for operations other than " +
592 "volume control");
593 Log.w(className, "See the documentation of " + opName + " for what to use instead with " +
Jean-Michel Trivi2f7511f2016-11-28 15:40:27 -0800594 "android.media.AudioAttributes to qualify your playback use case");
595 }
Jean-Michel Trivi3c86a342016-04-08 20:47:02 -0700596}