am 3d42d238: am f764d219: Merge "AudioService receives callback for dynamic policy mix state changes" into mnc-dev

* commit '3d42d2380f5af53ce299610e27f40a8d88930c89':
  AudioService receives callback for dynamic policy mix state changes
This commit is contained in:
Jean-Michel Trivi
2015-04-30 17:14:30 +00:00
committed by Android Git Automerger
7 changed files with 199 additions and 14 deletions

View File

@@ -111,6 +111,7 @@ static struct {
jfieldID mRouteFlags; jfieldID mRouteFlags;
jfieldID mRegistrationId; jfieldID mRegistrationId;
jfieldID mMixType; jfieldID mMixType;
jfieldID mCallbackFlags;
} gAudioMixFields; } gAudioMixFields;
static jclass gAudioFormatClass; static jclass gAudioFormatClass;
@@ -149,6 +150,10 @@ static struct {
jmethodID postEventFromNative; jmethodID postEventFromNative;
} gAudioPortEventHandlerMethods; } gAudioPortEventHandlerMethods;
static struct {
jmethodID postDynPolicyEventFromNative;
} gDynPolicyEventHandlerMethods;
static Mutex gLock; static Mutex gLock;
enum AudioError { enum AudioError {
@@ -166,7 +171,7 @@ enum {
#define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5 #define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ref-counted object for callbacks // ref-counted object for audio port callbacks
class JNIAudioPortCallback: public AudioSystem::AudioPortCallback class JNIAudioPortCallback: public AudioSystem::AudioPortCallback
{ {
public: public:
@@ -361,6 +366,26 @@ android_media_AudioSystem_error_callback(status_t err)
env->DeleteLocalRef(clazz); env->DeleteLocalRef(clazz);
} }
static void
android_media_AudioSystem_dyn_policy_callback(int event, String8 regId, int val)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (env == NULL) {
return;
}
jclass clazz = env->FindClass(kClassPathName);
const char* zechars = regId.string();
jstring zestring = env->NewStringUTF(zechars);
env->CallStaticVoidMethod(clazz, gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative,
event, zestring, val);
env->ReleaseStringUTFChars(zestring, zechars);
env->DeleteLocalRef(clazz);
}
static jint static jint
android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address, jstring device_name) android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address, jstring device_name)
{ {
@@ -1402,7 +1427,11 @@ android_media_AudioSystem_getAudioHwSyncForSession(JNIEnv *env, jobject thiz, ji
return (jint)AudioSystem::getAudioHwSyncForSession((audio_session_t)sessionId); return (jint)AudioSystem::getAudioHwSyncForSession((audio_session_t)sessionId);
} }
static void
android_media_AudioSystem_registerDynPolicyCallback(JNIEnv *env, jobject thiz)
{
AudioSystem::setDynPolicyCallback(android_media_AudioSystem_dyn_policy_callback);
}
static jint convertAudioMixToNative(JNIEnv *env, static jint convertAudioMixToNative(JNIEnv *env,
@@ -1419,6 +1448,8 @@ static jint convertAudioMixToNative(JNIEnv *env,
env->ReleaseStringUTFChars(jRegistrationId, nRegistrationId); env->ReleaseStringUTFChars(jRegistrationId, nRegistrationId);
env->DeleteLocalRef(jRegistrationId); env->DeleteLocalRef(jRegistrationId);
nAudioMix->mCbFlags = env->GetIntField(jAudioMix, gAudioMixFields.mCallbackFlags);
jobject jFormat = env->GetObjectField(jAudioMix, gAudioMixFields.mFormat); jobject jFormat = env->GetObjectField(jAudioMix, gAudioMixFields.mFormat);
nAudioMix->mFormat.sample_rate = env->GetIntField(jFormat, nAudioMix->mFormat.sample_rate = env->GetIntField(jFormat,
gAudioFormatFields.mSampleRate); gAudioFormatFields.mSampleRate);
@@ -1567,7 +1598,8 @@ static JNINativeMethod gMethods[] = {
(void *)android_media_AudioSystem_getAudioHwSyncForSession}, (void *)android_media_AudioSystem_getAudioHwSyncForSession},
{"registerPolicyMixes", "(Ljava/util/ArrayList;Z)I", {"registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
(void *)android_media_AudioSystem_registerPolicyMixes}, (void *)android_media_AudioSystem_registerPolicyMixes},
{"native_register_dynamic_policy_callback", "()V",
(void *)android_media_AudioSystem_registerDynPolicyCallback},
}; };
@@ -1670,6 +1702,10 @@ int register_android_media_AudioSystem(JNIEnv *env)
gEventHandlerFields.mJniCallback = GetFieldIDOrDie(env, gEventHandlerFields.mJniCallback = GetFieldIDOrDie(env,
eventHandlerClass, "mJniCallback", "J"); eventHandlerClass, "mJniCallback", "J");
gDynPolicyEventHandlerMethods.postDynPolicyEventFromNative =
GetStaticMethodIDOrDie(env, env->FindClass(kClassPathName),
"dynamicPolicyCallbackFromNative", "(ILjava/lang/String;I)V");
jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix"); jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass); gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
gAudioMixFields.mRule = GetFieldIDOrDie(env, audioMixClass, "mRule", gAudioMixFields.mRule = GetFieldIDOrDie(env, audioMixClass, "mRule",
@@ -1680,6 +1716,7 @@ int register_android_media_AudioSystem(JNIEnv *env)
gAudioMixFields.mRegistrationId = GetFieldIDOrDie(env, audioMixClass, "mRegistrationId", gAudioMixFields.mRegistrationId = GetFieldIDOrDie(env, audioMixClass, "mRegistrationId",
"Ljava/lang/String;"); "Ljava/lang/String;");
gAudioMixFields.mMixType = GetFieldIDOrDie(env, audioMixClass, "mMixType", "I"); gAudioMixFields.mMixType = GetFieldIDOrDie(env, audioMixClass, "mMixType", "I");
gAudioMixFields.mCallbackFlags = GetFieldIDOrDie(env, audioMixClass, "mCallbackFlags", "I");
jclass audioFormatClass = FindClassOrDie(env, "android/media/AudioFormat"); jclass audioFormatClass = FindClassOrDie(env, "android/media/AudioFormat");
gAudioFormatClass = MakeGlobalRefOrDie(env, audioFormatClass); gAudioFormatClass = MakeGlobalRefOrDie(env, audioFormatClass);

View File

@@ -19,6 +19,7 @@ package android.media;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioMix;
import android.util.Log;
import java.util.ArrayList; import java.util.ArrayList;
@@ -32,6 +33,7 @@ import java.util.ArrayList;
*/ */
public class AudioSystem public class AudioSystem
{ {
private static final String TAG = "AudioSystem";
/* These values must be kept in sync with system/audio.h */ /* These values must be kept in sync with system/audio.h */
/* /*
* If these are modified, please also update Settings.System.VOLUME_SETTINGS * If these are modified, please also update Settings.System.VOLUME_SETTINGS
@@ -224,6 +226,48 @@ public class AudioSystem
} }
} }
/**
* Handles events for the audio policy manager about dynamic audio policies
* @see android.media.audiopolicy.AudioPolicy
*/
public interface DynamicPolicyCallback
{
void onDynamicPolicyMixStateUpdate(String regId, int state);
}
//keep in sync with include/media/AudioPolicy.h
private final static int DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE = 0;
private static DynamicPolicyCallback sDynPolicyCallback;
public static void setDynamicPolicyCallback(DynamicPolicyCallback cb)
{
synchronized (AudioSystem.class) {
sDynPolicyCallback = cb;
native_register_dynamic_policy_callback();
}
}
private static void dynamicPolicyCallbackFromNative(int event, String regId, int val)
{
DynamicPolicyCallback cb = null;
synchronized (AudioSystem.class) {
if (sDynPolicyCallback != null) {
cb = sDynPolicyCallback;
}
}
if (cb != null) {
switch(event) {
case DYNAMIC_POLICY_EVENT_MIX_STATE_UPDATE:
cb.onDynamicPolicyMixStateUpdate(regId, val);
break;
default:
Log.e(TAG, "dynamicPolicyCallbackFromNative: unknown event " + event);
}
}
}
/* /*
* Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...) * Error codes used by public APIs (AudioTrack, AudioRecord, AudioManager ...)
* Must be kept in sync with frameworks/base/core/jni/android_media_AudioErrors.h * Must be kept in sync with frameworks/base/core/jni/android_media_AudioErrors.h
@@ -580,6 +624,9 @@ public class AudioSystem
public static native int listAudioPatches(ArrayList<AudioPatch> patches, int[] generation); public static native int listAudioPatches(ArrayList<AudioPatch> patches, int[] generation);
public static native int setAudioPortConfig(AudioPortConfig config); public static native int setAudioPortConfig(AudioPortConfig config);
// declare this instance as having a dynamic policy callback handler
private static native final void native_register_dynamic_policy_callback();
// must be kept in sync with value in include/system/audio.h // must be kept in sync with value in include/system/audio.h
public static final int AUDIO_HW_SYNC_INVALID = 0; public static final int AUDIO_HW_SYNC_INVALID = 0;

View File

@@ -36,20 +36,30 @@ public class AudioMix {
private int mRouteFlags; private int mRouteFlags;
private String mRegistrationId; private String mRegistrationId;
private int mMixType = MIX_TYPE_INVALID; private int mMixType = MIX_TYPE_INVALID;
private int mMixState = MIX_STATE_DISABLED; int mMixState = MIX_STATE_DISABLED;
int mCallbackFlags;
/** /**
* All parameters are guaranteed valid through the Builder. * All parameters are guaranteed valid through the Builder.
*/ */
private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags) { private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags, int callbackFlags) {
mRule = rule; mRule = rule;
mFormat = format; mFormat = format;
mRouteFlags = routeFlags; mRouteFlags = routeFlags;
mRegistrationId = null; mRegistrationId = null;
mMixType = rule.getTargetMixType(); mMixType = rule.getTargetMixType();
mCallbackFlags = callbackFlags;
} }
// ROUTE_FLAG_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h // CALLBACK_FLAG_* values: keep in sync with AudioMix::kCbFlag* values defined
// in frameworks/av/include/media/AudioPolicy.h
/** @hide */
public final static int CALLBACK_FLAG_NOTIFY_ACTIVITY = 0x1;
// when adding new MIX_FLAG_* flags, add them to this mask of authorized masks:
private final static int CALLBACK_FLAGS_ALL = CALLBACK_FLAG_NOTIFY_ACTIVITY;
// ROUTE_FLAG_* values: keep in sync with MIX_ROUTE_FLAG_* values defined
// in frameworks/av/include/media/AudioPolicy.h
/** /**
* An audio mix behavior where the output of the mix is sent to the original destination of * An audio mix behavior where the output of the mix is sent to the original destination of
* the audio signal, i.e. an output device for an output mix, or a recording for an input mix. * the audio signal, i.e. an output device for an output mix, or a recording for an input mix.
@@ -161,6 +171,7 @@ public class AudioMix {
private AudioMixingRule mRule = null; private AudioMixingRule mRule = null;
private AudioFormat mFormat = null; private AudioFormat mFormat = null;
private int mRouteFlags = 0; private int mRouteFlags = 0;
private int mCallbackFlags = 0;
/** /**
* @hide * @hide
@@ -198,6 +209,22 @@ public class AudioMix {
return this; return this;
} }
/**
* @hide
* Only used by AudioPolicyConfig, not a public API.
* @param callbackFlags which callbacks are called from native
* @return the same Builder instance.
* @throws IllegalArgumentException
*/
public Builder setCallbackFlags(int flags) throws IllegalArgumentException {
if ((flags != 0) && ((flags & CALLBACK_FLAGS_ALL) == 0)) {
throw new IllegalArgumentException("Illegal callback flags 0x"
+ Integer.toHexString(flags).toUpperCase());
}
mCallbackFlags = flags;
return this;
}
/** /**
* Sets the {@link AudioFormat} for the mix. * Sets the {@link AudioFormat} for the mix.
* @param format a non-null {@link AudioFormat} instance. * @param format a non-null {@link AudioFormat} instance.
@@ -256,7 +283,7 @@ public class AudioMix {
} }
mFormat = new AudioFormat.Builder().setSampleRate(rate).build(); mFormat = new AudioFormat.Builder().setSampleRate(rate).build();
} }
return new AudioMix(mRule, mFormat, mRouteFlags); return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags);
} }
} }
} }

View File

@@ -189,6 +189,12 @@ public class AudioPolicy {
@SystemApi @SystemApi
public AudioPolicy build() { public AudioPolicy build() {
if (mStatusListener != null) {
// the AudioPolicy status listener includes updates on each mix activity state
for (AudioMix mix : mMixes) {
mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY;
}
}
return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper, return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
mFocusListener, mStatusListener); mFocusListener, mStatusListener);
} }
@@ -432,6 +438,18 @@ public class AudioPolicy {
+ afi.getClientId() + "wasNotified=" + wasNotified); + afi.getClientId() + "wasNotified=" + wasNotified);
} }
} }
public void notifyMixStateUpdate(String regId, int state) {
for (AudioMix mix : mConfig.getMixes()) {
if (mix.getRegistration().equals(regId)) {
mix.mMixState = state;
sendMsg(MSG_MIX_STATE_UPDATE, mix, 0/*ignored*/);
if (DEBUG) {
Log.v(TAG, "notifyMixStateUpdate: regId=" + regId + " state=" + state);
}
}
}
}
}; };
//================================================== //==================================================
@@ -440,6 +458,7 @@ public class AudioPolicy {
private final static int MSG_POLICY_STATUS_CHANGE = 0; private final static int MSG_POLICY_STATUS_CHANGE = 0;
private final static int MSG_FOCUS_GRANT = 1; private final static int MSG_FOCUS_GRANT = 1;
private final static int MSG_FOCUS_LOSS = 2; private final static int MSG_FOCUS_LOSS = 2;
private final static int MSG_MIX_STATE_UPDATE = 3;
private class EventHandler extends Handler { private class EventHandler extends Handler {
public EventHandler(AudioPolicy ap, Looper looper) { public EventHandler(AudioPolicy ap, Looper looper) {
@@ -464,6 +483,11 @@ public class AudioPolicy {
(AudioFocusInfo) msg.obj, msg.arg1 != 0); (AudioFocusInfo) msg.obj, msg.arg1 != 0);
} }
break; break;
case MSG_MIX_STATE_UPDATE:
if (mStatusListener != null) {
mStatusListener.onMixStateUpdate((AudioMix) msg.obj);
}
break;
default: default:
Log.e(TAG, "Unknown event " + msg.what); Log.e(TAG, "Unknown event " + msg.what);
} }

View File

@@ -59,6 +59,10 @@ public class AudioPolicyConfig implements Parcelable {
mMixes.add(mix); mMixes.add(mix);
} }
public ArrayList<AudioMix> getMixes() {
return mMixes;
}
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(mMixes); return Objects.hash(mMixes);
@@ -75,6 +79,8 @@ public class AudioPolicyConfig implements Parcelable {
for (AudioMix mix : mMixes) { for (AudioMix mix : mMixes) {
// write mix route flags // write mix route flags
dest.writeInt(mix.getRouteFlags()); dest.writeInt(mix.getRouteFlags());
// write callback flags
dest.writeInt(mix.mCallbackFlags);
// write mix format // write mix format
dest.writeInt(mix.getFormat().getSampleRate()); dest.writeInt(mix.getFormat().getSampleRate());
dest.writeInt(mix.getFormat().getEncoding()); dest.writeInt(mix.getFormat().getEncoding());
@@ -96,6 +102,8 @@ public class AudioPolicyConfig implements Parcelable {
// read mix route flags // read mix route flags
int routeFlags = in.readInt(); int routeFlags = in.readInt();
mixBuilder.setRouteFlags(routeFlags); mixBuilder.setRouteFlags(routeFlags);
// read callback flags
mixBuilder.setCallbackFlags(in.readInt());
// read mix format // read mix format
int sampleRate = in.readInt(); int sampleRate = in.readInt();
int encoding = in.readInt(); int encoding = in.readInt();

View File

@@ -25,4 +25,7 @@ oneway interface IAudioPolicyCallback {
// callbacks for audio focus // callbacks for audio focus
void notifyAudioFocusGrant(in AudioFocusInfo afi, int requestResult); void notifyAudioFocusGrant(in AudioFocusInfo afi, int requestResult);
void notifyAudioFocusLoss(in AudioFocusInfo afi, boolean wasNotified); void notifyAudioFocusLoss(in AudioFocusInfo afi, boolean wasNotified);
// callback for mix activity status update
void notifyMixStateUpdate(in String regId, int state);
} }

View File

@@ -65,6 +65,7 @@ import android.media.SoundPool;
import android.media.VolumePolicy; import android.media.VolumePolicy;
import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnErrorListener;
import android.media.audiopolicy.AudioMix;
import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.AudioPolicyConfig; import android.media.audiopolicy.AudioPolicyConfig;
import android.media.audiopolicy.IAudioPolicyCallback; import android.media.audiopolicy.IAudioPolicyCallback;
@@ -206,6 +207,7 @@ public class AudioService extends IAudioService.Stub {
private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = 22; private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = 22;
private static final int MSG_PERSIST_MICROPHONE_MUTE = 23; private static final int MSG_PERSIST_MICROPHONE_MUTE = 23;
private static final int MSG_UNMUTE_STREAM = 24; private static final int MSG_UNMUTE_STREAM = 24;
private static final int MSG_DYN_POLICY_MIX_STATE_UPDATE = 25;
// start of messages handled under wakelock // start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -4337,6 +4339,9 @@ public class AudioService extends IAudioService.Stub {
case MSG_UNMUTE_STREAM: case MSG_UNMUTE_STREAM:
onUnmuteStream(msg.arg1, msg.arg2); onUnmuteStream(msg.arg1, msg.arg2);
break; break;
case MSG_DYN_POLICY_MIX_STATE_UPDATE:
onDynPolicyMixStateUpdate((String) msg.obj, msg.arg1);
break;
} }
} }
} }
@@ -5758,6 +5763,8 @@ public class AudioService extends IAudioService.Stub {
//========================================================================================== //==========================================================================================
public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb, public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb,
boolean hasFocusListener) { boolean hasFocusListener) {
AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);
if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder() if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder()
+ " with config:" + policyConfig); + " with config:" + policyConfig);
String regId = null; String regId = null;
@@ -5852,6 +5859,39 @@ public class AudioService extends IAudioService.Stub {
} }
} }
//======================
// Audio policy callback from AudioSystem
//======================
private final AudioSystem.DynamicPolicyCallback mDynPolicyCallback =
new AudioSystem.DynamicPolicyCallback() {
public void onDynamicPolicyMixStateUpdate(String regId, int state) {
if (!TextUtils.isEmpty(regId)) {
sendMsg(mAudioHandler, MSG_DYN_POLICY_MIX_STATE_UPDATE, SENDMSG_QUEUE,
state /*arg1*/, 0 /*arg2 ignored*/, regId /*obj*/, 0 /*delay*/);
}
}
};
private void onDynPolicyMixStateUpdate(String regId, int state) {
if (DEBUG_AP) Log.d(TAG, "onDynamicPolicyMixStateUpdate("+ regId + ", " + state +")");
synchronized (mAudioPolicies) {
for (AudioPolicyProxy policy : mAudioPolicies.values()) {
for (AudioMix mix : policy.getMixes()) {
if (mix.getRegistration().equals(regId)) {
try {
policy.mPolicyCallback.notifyMixStateUpdate(regId, state);
} catch (RemoteException e) {
Log.e(TAG, "Can't call notifyMixStateUpdate() on IAudioPolicyCallback "
+ policy.mPolicyCallback.asBinder(), e);
}
return;
}
}
}
}
}
//====================== //======================
// Audio policy proxy // Audio policy proxy
//====================== //======================
@@ -5861,8 +5901,7 @@ public class AudioService extends IAudioService.Stub {
*/ */
public class AudioPolicyProxy extends AudioPolicyConfig implements IBinder.DeathRecipient { public class AudioPolicyProxy extends AudioPolicyConfig implements IBinder.DeathRecipient {
private static final String TAG = "AudioPolicyProxy"; private static final String TAG = "AudioPolicyProxy";
AudioPolicyConfig mConfig; IAudioPolicyCallback mPolicyCallback;
IAudioPolicyCallback mPolicyToken;
boolean mHasFocusListener; boolean mHasFocusListener;
/** /**
* Audio focus ducking behavior for an audio policy. * Audio focus ducking behavior for an audio policy.
@@ -5877,19 +5916,19 @@ public class AudioService extends IAudioService.Stub {
boolean hasFocusListener) { boolean hasFocusListener) {
super(config); super(config);
setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++)); setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++));
mPolicyToken = token; mPolicyCallback = token;
mHasFocusListener = hasFocusListener; mHasFocusListener = hasFocusListener;
if (mHasFocusListener) { if (mHasFocusListener) {
mMediaFocusControl.addFocusFollower(mPolicyToken); mMediaFocusControl.addFocusFollower(mPolicyCallback);
} }
connectMixes(); connectMixes();
} }
public void binderDied() { public void binderDied() {
synchronized (mAudioPolicies) { synchronized (mAudioPolicies) {
Log.i(TAG, "audio policy " + mPolicyToken + " died"); Log.i(TAG, "audio policy " + mPolicyCallback + " died");
release(); release();
mAudioPolicies.remove(mPolicyToken.asBinder()); mAudioPolicies.remove(mPolicyCallback.asBinder());
} }
} }
@@ -5902,7 +5941,7 @@ public class AudioService extends IAudioService.Stub {
mMediaFocusControl.setDuckingInExtPolicyAvailable(false); mMediaFocusControl.setDuckingInExtPolicyAvailable(false);
} }
if (mHasFocusListener) { if (mHasFocusListener) {
mMediaFocusControl.removeFocusFollower(mPolicyToken); mMediaFocusControl.removeFocusFollower(mPolicyCallback);
} }
AudioSystem.registerPolicyMixes(mMixes, false); AudioSystem.registerPolicyMixes(mMixes, false);
} }