13c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi/*
23c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi * Copyright (C) 2016 The Android Open Source Project
33c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi *
43c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi * Licensed under the Apache License, Version 2.0 (the "License");
53c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi * you may not use this file except in compliance with the License.
63c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi * You may obtain a copy of the License at
73c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi *
83c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi *      http://www.apache.org/licenses/LICENSE-2.0
93c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi *
103c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi * Unless required by applicable law or agreed to in writing, software
113c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi * distributed under the License is distributed on an "AS IS" BASIS,
123c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi * See the License for the specific language governing permissions and
143c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi * limitations under the License.
153c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi */
163c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
173c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivipackage android.media;
183c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
193c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Triviimport android.annotation.NonNull;
20035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hungimport android.annotation.Nullable;
213c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Triviimport android.app.ActivityThread;
223c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Triviimport android.app.AppOpsManager;
233c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Triviimport android.content.Context;
24035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hungimport android.media.VolumeShaper;
25292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Triviimport android.os.Binder;
263c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Triviimport android.os.IBinder;
27292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Triviimport android.os.Parcel;
28292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Triviimport android.os.Parcelable;
293c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Triviimport android.os.Process;
303c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Triviimport android.os.RemoteException;
313c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Triviimport android.os.ServiceManager;
323c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Triviimport android.util.Log;
333c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
3476e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Triviimport com.android.internal.annotations.GuardedBy;
353c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Triviimport com.android.internal.app.IAppOpsCallback;
363c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Triviimport com.android.internal.app.IAppOpsService;
373c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
38292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Triviimport java.lang.IllegalArgumentException;
39cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Triviimport java.lang.ref.WeakReference;
40292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Triviimport java.util.Objects;
41292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
423c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi/**
433c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi * Class to encapsulate a number of common player operations:
443c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi *   - AppOps for OP_PLAY_AUDIO
453c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi *   - more to come (routing, transport control)
463c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi * @hide
473c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi */
483c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivipublic abstract class PlayerBase {
493c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
50aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi    private static final String TAG = "PlayerBase";
518e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi    /** Debug app ops */
52aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi    private static final boolean DEBUG_APP_OPS = false;
53b8748c65b8ccc645eeec95e8922058238e72cc9dJean-Michel Trivi    private static final boolean DEBUG = DEBUG_APP_OPS || false;
54b8748c65b8ccc645eeec95e8922058238e72cc9dJean-Michel Trivi    private static IAudioService sService; //lazy initialization, use getService()
558e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi
563c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    // parameters of the player that affect AppOps
573c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    protected AudioAttributes mAttributes;
583c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    protected float mLeftVolume = 1.0f;
593c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    protected float mRightVolume = 1.0f;
603c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    protected float mAuxEffectSendLevel = 0.0f;
613c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
6276e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    // NEVER call into AudioService (see getService()) with mLock held: PlayerBase can run in
6376e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    // the same process as AudioService, which can synchronously call back into this class,
6476e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    // causing deadlocks between the two
6576e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    private final Object mLock = new Object();
6676e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi
673c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    // for AppOps
6876e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    private @Nullable IAppOpsService mAppOps;
699dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi    private IAppOpsCallback mAppOpsCallback;
7076e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    @GuardedBy("mLock")
7176e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    private boolean mHasAppOpsPlayAudio = true;
723c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
73292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    private final int mImplType;
74292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    // uniquely identifies the Player Interface throughout the system (P I Id)
7576e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    private int mPlayerIId = AudioPlaybackConfiguration.PLAYER_PIID_UNASSIGNED;
76292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
7776e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    @GuardedBy("mLock")
7876e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    private int mState;
7976e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    @GuardedBy("mLock")
8076e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    private int mStartDelayMs = 0;
8176e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    @GuardedBy("mLock")
8276e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    private float mPanMultiplierL = 1.0f;
8376e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    @GuardedBy("mLock")
8476e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    private float mPanMultiplierR = 1.0f;
85292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
863c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    /**
873c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * Constructor. Must be given audio attributes, as they are required for AppOps.
883c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * @param attr non-null audio attributes
89292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi     * @param class non-null class of the implementation of this abstract class
903c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     */
91292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    PlayerBase(@NonNull AudioAttributes attr, int implType) {
923c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        if (attr == null) {
933c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            throw new IllegalArgumentException("Illegal null AudioAttributes");
943c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        }
953c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        mAttributes = attr;
96292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        mImplType = implType;
97e9630038872065874c66c8f120555cf09d1ee060Jean-Michel Trivi        mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE;
989dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi    };
999dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi
1009dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi    /**
1019dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi     * Call from derived class when instantiation / initialization is successful
1029dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi     */
1039dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi    protected void baseRegisterPlayer() {
10444a8f53f94808fdc5ac35a249d21ff2ba23e9419Jean-Michel Trivi        int newPiid = AudioPlaybackConfiguration.PLAYER_PIID_INVALID;
1053c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
1063c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        mAppOps = IAppOpsService.Stub.asInterface(b);
1073c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        // initialize mHasAppOpsPlayAudio
108aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        updateAppOpsPlayAudio();
1093c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed
110aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        mAppOpsCallback = new IAppOpsCallbackWrapper(this);
1113c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        try {
1123c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,
1133c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi                    ActivityThread.currentPackageName(), mAppOpsCallback);
1143c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        } catch (RemoteException e) {
115b8748c65b8ccc645eeec95e8922058238e72cc9dJean-Michel Trivi            Log.e(TAG, "Error registering appOps callback", e);
1163c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            mHasAppOpsPlayAudio = false;
1173c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        }
118292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        try {
119cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            newPiid = getService().trackPlayer(
120cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi                    new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
121292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        } catch (RemoteException e) {
122292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
123292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        }
12444a8f53f94808fdc5ac35a249d21ff2ba23e9419Jean-Michel Trivi        mPlayerIId = newPiid;
1253c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    }
1263c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
1273c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    /**
1283c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * To be called whenever the audio attributes of the player change
1293c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * @param attr non-null audio attributes
1303c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     */
1313c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    void baseUpdateAudioAttributes(@NonNull AudioAttributes attr) {
1323c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        if (attr == null) {
1333c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            throw new IllegalArgumentException("Illegal null AudioAttributes");
1343c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        }
135292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        try {
136292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            getService().playerAttributes(mPlayerIId, attr);
137292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        } catch (RemoteException e) {
138292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e);
139292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        }
14099489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        synchronized (mLock) {
141e5a351cb9213b59026efd602011a4d9e99c85649Eric Laurent            boolean attributesChanged = (mAttributes != attr);
1423c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            mAttributes = attr;
143e5a351cb9213b59026efd602011a4d9e99c85649Eric Laurent            updateAppOpsPlayAudio_sync(attributesChanged);
1443c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        }
1453c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    }
1463c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
14776e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    private void updateState(int state) {
14876e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi        final int piid;
14976e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi        synchronized (mLock) {
15076e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi            mState = state;
15176e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi            piid = mPlayerIId;
15276e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi        }
153292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        try {
15476e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi            getService().playerEvent(piid, state);
155292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        } catch (RemoteException e) {
15676e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi            Log.e(TAG, "Error talking to audio service, "
15776e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi                    + AudioPlaybackConfiguration.toLogFriendlyPlayerState(state)
15876e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi                    + " state will not be tracked for piid=" + piid, e);
159292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        }
16076e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    }
16176e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi
16276e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi    void baseStart() {
16376e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi        if (DEBUG) { Log.v(TAG, "baseStart() piid=" + mPlayerIId); }
16476e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi        updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
16599489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        synchronized (mLock) {
1663c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            if (isRestricted_sync()) {
1678e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                playerSetVolume(true/*muting*/,0, 0);
1683c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            }
1693c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        }
1703c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    }
1713c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
17299489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi    void baseSetStartDelayMs(int delayMs) {
17399489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        synchronized(mLock) {
17499489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi            mStartDelayMs = Math.max(delayMs, 0);
17599489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        }
17699489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi    }
17799489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi
17899489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi    protected int getStartDelayMs() {
17999489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        synchronized(mLock) {
18099489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi            return mStartDelayMs;
18199489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        }
18299489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi    }
18399489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi
184292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    void basePause() {
185292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        if (DEBUG) { Log.v(TAG, "basePause() piid=" + mPlayerIId); }
18676e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi        updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED);
187292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    }
188292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
189292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    void baseStop() {
190292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        if (DEBUG) { Log.v(TAG, "baseStop() piid=" + mPlayerIId); }
19176e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi        updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED);
192292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    }
193292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
19499489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi    void baseSetPan(float pan) {
19599489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        final float p = Math.min(Math.max(-1.0f, pan), 1.0f);
19699489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        synchronized (mLock) {
19799489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi            if (p >= 0.0f) {
19899489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi                mPanMultiplierL = 1.0f - p;
19999489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi                mPanMultiplierR = 1.0f;
20099489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi            } else {
20199489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi                mPanMultiplierL = 1.0f;
20299489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi                mPanMultiplierR = 1.0f + p;
20399489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi            }
20499489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        }
20599489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        baseSetVolume(mLeftVolume, mRightVolume);
20699489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi    }
20799489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi
2083c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    void baseSetVolume(float leftVolume, float rightVolume) {
209e5a351cb9213b59026efd602011a4d9e99c85649Eric Laurent        final boolean isRestricted;
21099489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        synchronized (mLock) {
2113c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            mLeftVolume = leftVolume;
2123c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            mRightVolume = rightVolume;
213e5a351cb9213b59026efd602011a4d9e99c85649Eric Laurent            isRestricted = isRestricted_sync();
2143c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        }
215e5a351cb9213b59026efd602011a4d9e99c85649Eric Laurent        playerSetVolume(isRestricted/*muting*/,
21699489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi                leftVolume * mPanMultiplierL, rightVolume * mPanMultiplierR);
2173c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    }
2183c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
2193c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    int baseSetAuxEffectSendLevel(float level) {
22099489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        synchronized (mLock) {
2213c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            mAuxEffectSendLevel = level;
2223c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            if (isRestricted_sync()) {
2233c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi                return AudioSystem.SUCCESS;
2243c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            }
2253c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        }
2268e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi        return playerSetAuxEffectSendLevel(false/*muting*/, level);
2273c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    }
2283c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
2293c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    /**
2303c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * To be called from a subclass release or finalize method.
2313c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * Releases AppOps related resources.
2323c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     */
2333c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    void baseRelease() {
234e9630038872065874c66c8f120555cf09d1ee060Jean-Michel Trivi        if (DEBUG) { Log.v(TAG, "baseRelease() piid=" + mPlayerIId + " state=" + mState); }
23576e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi        boolean releasePlayer = false;
23676e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi        synchronized (mLock) {
23776e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi            if (mState != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) {
23876e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi                releasePlayer = true;
23976e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi                mState = AudioPlaybackConfiguration.PLAYER_STATE_RELEASED;
24076e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi            }
24176e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi        }
242292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        try {
24376e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi            if (releasePlayer) {
24476e124bcd3ee2e5ba98dcbdb99965a4c133fe7e4Jean-Michel Trivi                getService().releasePlayer(mPlayerIId);
245292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            }
246292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        } catch (RemoteException e) {
247292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            Log.e(TAG, "Error talking to audio service, the player will still be tracked", e);
248292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        }
2493c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        try {
2500fbcb86389623e57689fcc3956ef94084ab9ceabJean-Michel Trivi            if (mAppOps != null) {
2510fbcb86389623e57689fcc3956ef94084ab9ceabJean-Michel Trivi                mAppOps.stopWatchingMode(mAppOpsCallback);
2520fbcb86389623e57689fcc3956ef94084ab9ceabJean-Michel Trivi            }
253a00f86009eb84a96a2086d3e3e065bd812cdcee3Jean-Michel Trivi        } catch (Exception e) {
2543c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            // nothing to do here, the object is supposed to be released anyway
2553c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        }
2563c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    }
2573c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
258aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi    private void updateAppOpsPlayAudio() {
259aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        synchronized (mLock) {
260e5a351cb9213b59026efd602011a4d9e99c85649Eric Laurent            updateAppOpsPlayAudio_sync(false);
261aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        }
262aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi    }
263aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi
2643c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    /**
2653c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * To be called whenever a condition that might affect audibility of this player is updated.
26699489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi     * Must be called synchronized on mLock.
2673c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     */
268e5a351cb9213b59026efd602011a4d9e99c85649Eric Laurent    void updateAppOpsPlayAudio_sync(boolean attributesChanged) {
2693c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio;
2703c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        try {
2710fbcb86389623e57689fcc3956ef94084ab9ceabJean-Michel Trivi            int mode = AppOpsManager.MODE_IGNORED;
2720fbcb86389623e57689fcc3956ef94084ab9ceabJean-Michel Trivi            if (mAppOps != null) {
2730fbcb86389623e57689fcc3956ef94084ab9ceabJean-Michel Trivi                mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
2743c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi                    mAttributes.getUsage(),
2753c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi                    Process.myUid(), ActivityThread.currentPackageName());
2760fbcb86389623e57689fcc3956ef94084ab9ceabJean-Michel Trivi            }
2773c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            mHasAppOpsPlayAudio = (mode == AppOpsManager.MODE_ALLOWED);
2783c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        } catch (RemoteException e) {
2793c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            mHasAppOpsPlayAudio = false;
2803c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        }
2813c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
2823c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        // AppsOps alters a player's volume; when the restriction changes, reflect it on the actual
2833c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        // volume used by the player
2843c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        try {
285e5a351cb9213b59026efd602011a4d9e99c85649Eric Laurent            if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio ||
286e5a351cb9213b59026efd602011a4d9e99c85649Eric Laurent                    attributesChanged) {
2873120059d5bdc52fb5ef2c90d9662562e92cd4df9Jean-Michel Trivi                getService().playerHasOpPlayAudio(mPlayerIId, mHasAppOpsPlayAudio);
288e5a351cb9213b59026efd602011a4d9e99c85649Eric Laurent                if (!isRestricted_sync()) {
2898e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                    if (DEBUG_APP_OPS) {
2908e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                        Log.v(TAG, "updateAppOpsPlayAudio: unmuting player, vol=" + mLeftVolume
2918e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                                + "/" + mRightVolume);
2928e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                    }
29399489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi                    playerSetVolume(false/*muting*/,
29499489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi                            mLeftVolume * mPanMultiplierL, mRightVolume * mPanMultiplierR);
2958e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                    playerSetAuxEffectSendLevel(false/*muting*/, mAuxEffectSendLevel);
2963c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi                } else {
2978e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                    if (DEBUG_APP_OPS) {
2988e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                        Log.v(TAG, "updateAppOpsPlayAudio: muting player");
2998e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                    }
3008e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                    playerSetVolume(true/*muting*/, 0.0f, 0.0f);
3018e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                    playerSetAuxEffectSendLevel(true/*muting*/, 0.0f);
3023c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi                }
3033c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            }
3043c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        } catch (Exception e) {
3053c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            // failing silently, player might not be in right state
3063c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        }
3073c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    }
3083c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
3093c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    /**
3103c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * To be called by the subclass whenever an operation is potentially restricted.
3113c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * As the media player-common behavior are incorporated into this class, the subclass's need
3123c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * to call this method should be removed, and this method could become private.
3133c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * FIXME can this method be private so subclasses don't have to worry about when to check
3143c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     *    the restrictions.
3153c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     * @return
3163c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi     */
3173c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    boolean isRestricted_sync() {
3181293c060c81de1124887ddc05603eb3f98b50cbaJean-Michel Trivi        // check app ops
3191293c060c81de1124887ddc05603eb3f98b50cbaJean-Michel Trivi        if (mHasAppOpsPlayAudio) {
3201293c060c81de1124887ddc05603eb3f98b50cbaJean-Michel Trivi            return false;
3211293c060c81de1124887ddc05603eb3f98b50cbaJean-Michel Trivi        }
3221293c060c81de1124887ddc05603eb3f98b50cbaJean-Michel Trivi        // check bypass flag
3233c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
3243c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi            return false;
3253c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi        }
3268e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi        // check force audibility flag and camera restriction
3278e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi        if (((mAttributes.getAllFlags() & AudioAttributes.FLAG_AUDIBILITY_ENFORCED) != 0)
3288e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                && (mAttributes.getUsage() == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)) {
3298e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi            boolean cameraSoundForced = false;
3308e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi            try {
3318e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                cameraSoundForced = getService().isCameraSoundForced();
3328e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi            } catch (RemoteException e) {
3338e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                Log.e(TAG, "Cannot access AudioService in isRestricted_sync()");
3348e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi            } catch (NullPointerException e) {
3358e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                Log.e(TAG, "Null AudioService in isRestricted_sync()");
3368e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi            }
3378e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi            if (cameraSoundForced) {
3388e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi                return false;
3398e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi            }
3408e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi        }
3411293c060c81de1124887ddc05603eb3f98b50cbaJean-Michel Trivi        return true;
3423c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    }
3433c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi
3448e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi    private static IAudioService getService()
3458e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi    {
3468e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi        if (sService != null) {
3478e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi            return sService;
3488e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi        }
3498e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
3508e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi        sService = IAudioService.Stub.asInterface(b);
3518e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi        return sService;
3528e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi    }
3538e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi
35499489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi    /**
35599489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi     * @hide
35699489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi     * @param delayMs
35799489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi     */
35899489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi    public void setStartDelayMs(int delayMs) {
35999489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        baseSetStartDelayMs(delayMs);
36099489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi    }
36199489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi
362292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    //=====================================================================
3633c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi    // Abstract methods a subclass needs to implement
3648e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi    /**
3658e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi     * Abstract method for the subclass behavior's for volume and muting commands
3668e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi     * @param muting if true, the player is to be muted, and the volume values can be ignored
3678e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi     * @param leftVolume the left volume to use if muting is false
3688e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi     * @param rightVolume the right volume to use if muting is false
3698e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi     */
3708e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi    abstract void playerSetVolume(boolean muting, float leftVolume, float rightVolume);
371035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung
372035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung    /**
373035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * Abstract method to apply a {@link VolumeShaper.Configuration}
374035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * and a {@link VolumeShaper.Operation} to the Player.
375035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * This should be overridden by the Player to call into the native
376035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * VolumeShaper implementation. Multiple {@code VolumeShapers} may be
377035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * concurrently active for a given Player, each accessible by the
378035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * {@code VolumeShaper} id.
379035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     *
380035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * The {@code VolumeShaper} implementation caches the id returned
381035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * when applying a fully specified configuration
382035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * from {VolumeShaper.Configuration.Builder} to track later
383035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * operation changes requested on it.
384035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     *
385035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * @param configuration a {@code VolumeShaper.Configuration} object
386035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     *        created by {@link VolumeShaper.Configuration.Builder} or
387035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     *        an created from a {@code VolumeShaper} id
388035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     *        by the {@link VolumeShaper.Configuration} constructor.
389035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * @param operation a {@code VolumeShaper.Operation}.
390035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * @return a negative error status or a
391035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     *         non-negative {@code VolumeShaper} id on success.
392035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     */
393035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung    /* package */ abstract int playerApplyVolumeShaper(
394035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung            @NonNull VolumeShaper.Configuration configuration,
395035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung            @NonNull VolumeShaper.Operation operation);
396035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung
397035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung    /**
398035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * Abstract method to get the current VolumeShaper state.
399035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * @param id the {@code VolumeShaper} id returned from
400035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     *           sending a fully specified {@code VolumeShaper.Configuration}
401035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     *           through {@link #playerApplyVolumeShaper}
402035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     * @return a {@code VolumeShaper.State} object or null if
403035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     *         there is no {@code VolumeShaper} for the id.
404035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung     */
405035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung    /* package */ abstract @Nullable VolumeShaper.State playerGetVolumeShaperState(int id);
406035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung
4078e48c6939bd81dcbc596f0a4b11bfaea73aed00bJean-Michel Trivi    abstract int playerSetAuxEffectSendLevel(boolean muting, float level);
4089dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi    abstract void playerStart();
4099dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi    abstract void playerPause();
4109dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi    abstract void playerStop();
4112f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi
4122f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi    //=====================================================================
413aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi    private static class IAppOpsCallbackWrapper extends IAppOpsCallback.Stub {
414aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        private final WeakReference<PlayerBase> mWeakPB;
415aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi
416aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        public IAppOpsCallbackWrapper(PlayerBase pb) {
417aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi            mWeakPB = new WeakReference<PlayerBase>(pb);
418aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        }
419aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi
420aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        @Override
421aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        public void opChanged(int op, int uid, String packageName) {
422aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi            if (op == AppOpsManager.OP_PLAY_AUDIO) {
423aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi                if (DEBUG_APP_OPS) { Log.v(TAG, "opChanged: op=PLAY_AUDIO pack=" + packageName); }
424aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi                final PlayerBase pb = mWeakPB.get();
425aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi                if (pb != null) {
426aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi                    pb.updateAppOpsPlayAudio();
427aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi                }
428aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi            }
429aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        }
430aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi    }
431aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi
432aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi    //=====================================================================
4339dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi    /**
434cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi     * Wrapper around an implementation of IPlayer for all subclasses of PlayerBase
435cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi     * that doesn't keep a strong reference on PlayerBase
4369dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi     */
437cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi    private static class IPlayerWrapper extends IPlayer.Stub {
438cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi        private final WeakReference<PlayerBase> mWeakPB;
439cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi
440cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi        public IPlayerWrapper(PlayerBase pb) {
441cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            mWeakPB = new WeakReference<PlayerBase>(pb);
442cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi        }
443cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi
444292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        @Override
4459dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi        public void start() {
446cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            final PlayerBase pb = mWeakPB.get();
447cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            if (pb != null) {
448cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi                pb.playerStart();
449cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            }
4509dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi        }
4519dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi
4529dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi        @Override
4539dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi        public void pause() {
454cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            final PlayerBase pb = mWeakPB.get();
455cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            if (pb != null) {
456cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi                pb.playerPause();
457cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            }
4589dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi        }
4599dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi
460292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        @Override
4619dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi        public void stop() {
462cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            final PlayerBase pb = mWeakPB.get();
463cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            if (pb != null) {
464cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi                pb.playerStop();
465cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            }
4669dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi        }
4679dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi
468292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        @Override
4699dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi        public void setVolume(float vol) {
470cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            final PlayerBase pb = mWeakPB.get();
471cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            if (pb != null) {
472cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi                pb.baseSetVolume(vol, vol);
473cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            }
4749dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi        }
47599489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi
47699489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        @Override
47799489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        public void setPan(float pan) {
478cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            final PlayerBase pb = mWeakPB.get();
479cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            if (pb != null) {
480cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi                pb.baseSetPan(pan);
481cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            }
48299489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        }
48399489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi
48499489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        @Override
48599489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        public void setStartDelayMs(int delayMs) {
486cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            final PlayerBase pb = mWeakPB.get();
487cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            if (pb != null) {
488cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi                pb.baseSetStartDelayMs(delayMs);
489cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            }
49099489ccf740d369193a8ffc7eeb4bbde6919bd65Jean-Michel Trivi        }
491035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung
492035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung        @Override
493035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung        public void applyVolumeShaper(
494035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung                @NonNull VolumeShaper.Configuration configuration,
495035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung                @NonNull VolumeShaper.Operation operation) {
496cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            final PlayerBase pb = mWeakPB.get();
497cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            if (pb != null) {
498cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi                pb.playerApplyVolumeShaper(configuration, operation);
499cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi            }
500035d4ec772b0cde2a8d4b05d2daa9b9cbe11e117Andy Hung        }
501cf1b224a789f8412211d22fba9551ce01e54be14Jean-Michel Trivi    }
502292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
503292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    //=====================================================================
504292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    /**
505292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi     * Class holding all the information about a player that needs to be known at registration time
506292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi     */
507292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    public static class PlayerIdCard implements Parcelable {
508292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        public final int mPlayerType;
509292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
510aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        public static final int AUDIO_ATTRIBUTES_NONE = 0;
511aee6ee94675d56e71a42d52b16b8d8e5fa6ea3ffJean-Michel Trivi        public static final int AUDIO_ATTRIBUTES_DEFINED = 1;
512292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        public final AudioAttributes mAttributes;
5139dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi        public final IPlayer mIPlayer;
514292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
5159dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi        PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer) {
516292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            mPlayerType = type;
517292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            mAttributes = attr;
5189dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi            mIPlayer = iplayer;
519292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        }
520292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
521292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        @Override
522292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        public int hashCode() {
52344a8f53f94808fdc5ac35a249d21ff2ba23e9419Jean-Michel Trivi            return Objects.hash(mPlayerType);
524292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        }
525292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
526292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        @Override
527292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        public int describeContents() {
528292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            return 0;
529292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        }
530292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
531292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        @Override
532292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        public void writeToParcel(Parcel dest, int flags) {
533292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            dest.writeInt(mPlayerType);
534292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            mAttributes.writeToParcel(dest, 0);
5359dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi            dest.writeStrongBinder(mIPlayer == null ? null : mIPlayer.asBinder());
536292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        }
537292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
538292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        public static final Parcelable.Creator<PlayerIdCard> CREATOR
539292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        = new Parcelable.Creator<PlayerIdCard>() {
540292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            /**
541292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi             * Rebuilds an PlayerIdCard previously stored with writeToParcel().
542292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi             * @param p Parcel object to read the PlayerIdCard from
543292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi             * @return a new PlayerIdCard created from the data in the parcel
544292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi             */
545292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            public PlayerIdCard createFromParcel(Parcel p) {
546292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi                return new PlayerIdCard(p);
547292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            }
548292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            public PlayerIdCard[] newArray(int size) {
549292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi                return new PlayerIdCard[size];
550292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            }
551292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        };
552292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
553292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        private PlayerIdCard(Parcel in) {
554292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            mPlayerType = in.readInt();
555292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            mAttributes = AudioAttributes.CREATOR.createFromParcel(in);
5569dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi            // IPlayer can be null if unmarshalling a Parcel coming from who knows where
5579dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi            final IBinder b = in.readStrongBinder();
5589dc22c227cb5c01136a6aa1b52c7dfa3383c0bd7Jean-Michel Trivi            mIPlayer = (b == null ? null : IPlayer.Stub.asInterface(b));
559292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        }
560292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
561292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        @Override
562292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        public boolean equals(Object o) {
563292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            if (this == o) return true;
564292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            if (o == null || !(o instanceof PlayerIdCard)) return false;
565292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
566292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi            PlayerIdCard that = (PlayerIdCard) o;
567292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
56844a8f53f94808fdc5ac35a249d21ff2ba23e9419Jean-Michel Trivi            // FIXME change to the binder player interface once supported as a member
56944a8f53f94808fdc5ac35a249d21ff2ba23e9419Jean-Michel Trivi            return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes));
570292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi        }
571292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    }
572292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi
573292a6a4e9934a94eea97b018befde3baed895f7dJean-Michel Trivi    //=====================================================================
5742f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi    // Utilities
5752f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi
5762f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi    /**
5772f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi     * Use to generate warning or exception in legacy code paths that allowed passing stream types
5782f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi     * to qualify audio playback.
5792f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi     * @param streamType the stream type to check
5802f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi     * @throws IllegalArgumentException
5812f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi     */
5822f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi    public static void deprecateStreamTypeForPlayback(int streamType, String className,
5832f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi            String opName) throws IllegalArgumentException {
5842f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi        // STREAM_ACCESSIBILITY was introduced at the same time the use of stream types
5852f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi        // for audio playback was deprecated, so it is not allowed at all to qualify a playback
5862f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi        // use case
5872f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi        if (streamType == AudioManager.STREAM_ACCESSIBILITY) {
5882f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi            throw new IllegalArgumentException("Use of STREAM_ACCESSIBILITY is reserved for "
5892f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi                    + "volume control");
5902f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi        }
591c4c13b44b816540af7fe99b40b276c1c6979a44cJean-Michel Trivi        Log.w(className, "Use of stream types is deprecated for operations other than " +
592c4c13b44b816540af7fe99b40b276c1c6979a44cJean-Michel Trivi                "volume control");
593c4c13b44b816540af7fe99b40b276c1c6979a44cJean-Michel Trivi        Log.w(className, "See the documentation of " + opName + " for what to use instead with " +
5942f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi                "android.media.AudioAttributes to qualify your playback use case");
5952f7511fd065aa121287da3d4b5b8bea3e528b0bcJean-Michel Trivi    }
5963c86a343dfca1b9e2e28c240dc894f60709e392cJean-Michel Trivi}
597