Merge "Audio focus: API for external audio focus policy" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
4d52ae9707
@ -22842,6 +22842,7 @@ package android.media {
|
|||||||
method public void adjustStreamVolume(int, int, int);
|
method public void adjustStreamVolume(int, int, int);
|
||||||
method public void adjustSuggestedStreamVolume(int, int, int);
|
method public void adjustSuggestedStreamVolume(int, int, int);
|
||||||
method public void adjustVolume(int, int);
|
method public void adjustVolume(int, int);
|
||||||
|
method public int dispatchAudioFocusChange(android.media.AudioFocusInfo, int, android.media.audiopolicy.AudioPolicy);
|
||||||
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
|
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
|
||||||
method public int generateAudioSessionId();
|
method public int generateAudioSessionId();
|
||||||
method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
|
method public java.util.List<android.media.AudioPlaybackConfiguration> getActivePlaybackConfigurations();
|
||||||
@ -25844,8 +25845,10 @@ package android.media.audiopolicy {
|
|||||||
|
|
||||||
public static abstract class AudioPolicy.AudioPolicyFocusListener {
|
public static abstract class AudioPolicy.AudioPolicyFocusListener {
|
||||||
ctor public AudioPolicy.AudioPolicyFocusListener();
|
ctor public AudioPolicy.AudioPolicyFocusListener();
|
||||||
|
method public void onAudioFocusAbandon(android.media.AudioFocusInfo);
|
||||||
method public void onAudioFocusGrant(android.media.AudioFocusInfo, int);
|
method public void onAudioFocusGrant(android.media.AudioFocusInfo, int);
|
||||||
method public void onAudioFocusLoss(android.media.AudioFocusInfo, boolean);
|
method public void onAudioFocusLoss(android.media.AudioFocusInfo, boolean);
|
||||||
|
method public void onAudioFocusRequest(android.media.AudioFocusInfo, int);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static abstract class AudioPolicy.AudioPolicyStatusListener {
|
public static abstract class AudioPolicy.AudioPolicyStatusListener {
|
||||||
@ -25860,6 +25863,7 @@ package android.media.audiopolicy {
|
|||||||
method public android.media.audiopolicy.AudioPolicy build();
|
method public android.media.audiopolicy.AudioPolicy build();
|
||||||
method public void setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener);
|
method public void setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener);
|
||||||
method public void setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener);
|
method public void setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener);
|
||||||
|
method public android.media.audiopolicy.AudioPolicy.Builder setIsAudioFocusPolicy(boolean);
|
||||||
method public android.media.audiopolicy.AudioPolicy.Builder setLooper(android.os.Looper) throws java.lang.IllegalArgumentException;
|
method public android.media.audiopolicy.AudioPolicy.Builder setLooper(android.os.Looper) throws java.lang.IllegalArgumentException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2538,6 +2538,44 @@ public class AudioManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
* Notifies an application with a focus listener of gain or loss of audio focus.
|
||||||
|
* This method can only be used by owners of an {@link AudioPolicy} configured with
|
||||||
|
* {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to true.
|
||||||
|
* @param afi the recipient of the focus change, that has previously requested audio focus, and
|
||||||
|
* that was received by the {@code AudioPolicy} through
|
||||||
|
* {@link AudioPolicy.AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}.
|
||||||
|
* @param focusChange one of focus gain types ({@link #AUDIOFOCUS_GAIN},
|
||||||
|
* {@link #AUDIOFOCUS_GAIN_TRANSIENT}, {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} or
|
||||||
|
* {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE})
|
||||||
|
* or one of the focus loss types ({@link AudioManager#AUDIOFOCUS_LOSS},
|
||||||
|
* {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT},
|
||||||
|
* or {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}).
|
||||||
|
* <br>For the focus gain, the change type should be the same as the app requested.
|
||||||
|
* @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
|
||||||
|
* @return {@link #AUDIOFOCUS_REQUEST_GRANTED} if the dispatch was successfully sent, or
|
||||||
|
* {@link #AUDIOFOCUS_REQUEST_FAILED} if the focus client didn't have a listener, or
|
||||||
|
* if there was an error sending the request.
|
||||||
|
* @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null.
|
||||||
|
*/
|
||||||
|
@SystemApi
|
||||||
|
public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange,
|
||||||
|
@NonNull AudioPolicy ap) {
|
||||||
|
if (afi == null) {
|
||||||
|
throw new NullPointerException("Illegal null AudioFocusInfo");
|
||||||
|
}
|
||||||
|
if (ap == null) {
|
||||||
|
throw new NullPointerException("Illegal null AudioPolicy");
|
||||||
|
}
|
||||||
|
final IAudioService service = getService();
|
||||||
|
try {
|
||||||
|
return service.dispatchFocusChange(afi, focusChange, ap.cb());
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
throw e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide
|
* @hide
|
||||||
* Used internally by telephony package to abandon audio focus, typically after a call or
|
* Used internally by telephony package to abandon audio focus, typically after a call or
|
||||||
@ -2548,7 +2586,7 @@ public class AudioManager {
|
|||||||
final IAudioService service = getService();
|
final IAudioService service = getService();
|
||||||
try {
|
try {
|
||||||
service.abandonAudioFocus(null, AudioSystem.IN_VOICE_COMM_FOCUS_ID,
|
service.abandonAudioFocus(null, AudioSystem.IN_VOICE_COMM_FOCUS_ID,
|
||||||
null /*AudioAttributes, legacy behavior*/);
|
null /*AudioAttributes, legacy behavior*/, getContext().getOpPackageName());
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
throw e.rethrowFromSystemServer();
|
throw e.rethrowFromSystemServer();
|
||||||
}
|
}
|
||||||
@ -2579,7 +2617,7 @@ public class AudioManager {
|
|||||||
final IAudioService service = getService();
|
final IAudioService service = getService();
|
||||||
try {
|
try {
|
||||||
status = service.abandonAudioFocus(mAudioFocusDispatcher,
|
status = service.abandonAudioFocus(mAudioFocusDispatcher,
|
||||||
getIdForAudioFocusListener(l), aa);
|
getIdForAudioFocusListener(l), aa, getContext().getOpPackageName());
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
throw e.rethrowFromSystemServer();
|
throw e.rethrowFromSystemServer();
|
||||||
}
|
}
|
||||||
@ -2787,7 +2825,7 @@ public class AudioManager {
|
|||||||
final IAudioService service = getService();
|
final IAudioService service = getService();
|
||||||
try {
|
try {
|
||||||
String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(),
|
String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(),
|
||||||
policy.hasFocusListener());
|
policy.hasFocusListener(), policy.isFocusPolicy());
|
||||||
if (regId == null) {
|
if (regId == null) {
|
||||||
return ERROR;
|
return ERROR;
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,6 +20,7 @@ import android.app.PendingIntent;
|
|||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.media.AudioAttributes;
|
import android.media.AudioAttributes;
|
||||||
|
import android.media.AudioFocusInfo;
|
||||||
import android.media.AudioPlaybackConfiguration;
|
import android.media.AudioPlaybackConfiguration;
|
||||||
import android.media.AudioRecordingConfiguration;
|
import android.media.AudioRecordingConfiguration;
|
||||||
import android.media.AudioRoutesInfo;
|
import android.media.AudioRoutesInfo;
|
||||||
@ -122,7 +123,8 @@ interface IAudioService {
|
|||||||
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
|
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
|
||||||
IAudioPolicyCallback pcb);
|
IAudioPolicyCallback pcb);
|
||||||
|
|
||||||
int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa);
|
int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa,
|
||||||
|
in String callingPackageName);
|
||||||
|
|
||||||
void unregisterAudioFocusClient(String clientId);
|
void unregisterAudioFocusClient(String clientId);
|
||||||
|
|
||||||
@ -164,7 +166,7 @@ interface IAudioService {
|
|||||||
boolean isHdmiSystemAudioSupported();
|
boolean isHdmiSystemAudioSupported();
|
||||||
|
|
||||||
String registerAudioPolicy(in AudioPolicyConfig policyConfig,
|
String registerAudioPolicy(in AudioPolicyConfig policyConfig,
|
||||||
in IAudioPolicyCallback pcb, boolean hasFocusListener);
|
in IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy);
|
||||||
|
|
||||||
oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb);
|
oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb);
|
||||||
|
|
||||||
@ -196,5 +198,8 @@ interface IAudioService {
|
|||||||
|
|
||||||
int getFocusRampTimeMs(in int focusGain, in AudioAttributes attr);
|
int getFocusRampTimeMs(in int focusGain, in AudioAttributes attr);
|
||||||
|
|
||||||
|
int dispatchFocusChange(in AudioFocusInfo afi, in int focusChange,
|
||||||
|
in IAudioPolicyCallback pcb);
|
||||||
|
|
||||||
// WARNING: read warning at top of file, it is recommended to add new methods at the end
|
// WARNING: read warning at top of file, it is recommended to add new methods at the end
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ public class AudioPolicy {
|
|||||||
private int mStatus;
|
private int mStatus;
|
||||||
private String mRegistrationId;
|
private String mRegistrationId;
|
||||||
private AudioPolicyStatusListener mStatusListener;
|
private AudioPolicyStatusListener mStatusListener;
|
||||||
|
private boolean mIsFocusPolicy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The behavior of a policy with regards to audio focus where it relies on the application
|
* The behavior of a policy with regards to audio focus where it relies on the application
|
||||||
@ -96,12 +97,14 @@ public class AudioPolicy {
|
|||||||
public AudioPolicyConfig getConfig() { return mConfig; }
|
public AudioPolicyConfig getConfig() { return mConfig; }
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public boolean hasFocusListener() { return mFocusListener != null; }
|
public boolean hasFocusListener() { return mFocusListener != null; }
|
||||||
|
/** @hide */
|
||||||
|
public boolean isFocusPolicy() { return mIsFocusPolicy; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The parameter is guaranteed non-null through the Builder
|
* The parameter is guaranteed non-null through the Builder
|
||||||
*/
|
*/
|
||||||
private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper,
|
private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper,
|
||||||
AudioPolicyFocusListener fl, AudioPolicyStatusListener sl) {
|
AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy) {
|
||||||
mConfig = config;
|
mConfig = config;
|
||||||
mStatus = POLICY_STATUS_UNREGISTERED;
|
mStatus = POLICY_STATUS_UNREGISTERED;
|
||||||
mContext = context;
|
mContext = context;
|
||||||
@ -116,10 +119,12 @@ public class AudioPolicy {
|
|||||||
}
|
}
|
||||||
mFocusListener = fl;
|
mFocusListener = fl;
|
||||||
mStatusListener = sl;
|
mStatusListener = sl;
|
||||||
|
mIsFocusPolicy = isFocusPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder class for {@link AudioPolicy} objects
|
* Builder class for {@link AudioPolicy} objects.
|
||||||
|
* By default the policy to be created doesn't govern audio focus decisions.
|
||||||
*/
|
*/
|
||||||
@SystemApi
|
@SystemApi
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
@ -128,6 +133,7 @@ public class AudioPolicy {
|
|||||||
private Looper mLooper;
|
private Looper mLooper;
|
||||||
private AudioPolicyFocusListener mFocusListener;
|
private AudioPolicyFocusListener mFocusListener;
|
||||||
private AudioPolicyStatusListener mStatusListener;
|
private AudioPolicyStatusListener mStatusListener;
|
||||||
|
private boolean mIsFocusPolicy = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new Builder with no audio mixes.
|
* Constructs a new Builder with no audio mixes.
|
||||||
@ -178,6 +184,21 @@ public class AudioPolicy {
|
|||||||
mFocusListener = l;
|
mFocusListener = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declares whether this policy will grant and deny audio focus through
|
||||||
|
* the {@link AudioPolicy.AudioPolicyStatusListener}.
|
||||||
|
* If set to {@code true}, it is mandatory to set an
|
||||||
|
* {@link AudioPolicy.AudioPolicyStatusListener} in order to successfully build
|
||||||
|
* an {@code AudioPolicy} instance.
|
||||||
|
* @param enforce true if the policy will govern audio focus decisions.
|
||||||
|
* @return the same Builder instance.
|
||||||
|
*/
|
||||||
|
@SystemApi
|
||||||
|
public Builder setIsAudioFocusPolicy(boolean isFocusPolicy) {
|
||||||
|
mIsFocusPolicy = isFocusPolicy;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the audio policy status listener.
|
* Sets the audio policy status listener.
|
||||||
* @param l a {@link AudioPolicy.AudioPolicyStatusListener}
|
* @param l a {@link AudioPolicy.AudioPolicyStatusListener}
|
||||||
@ -187,6 +208,14 @@ public class AudioPolicy {
|
|||||||
mStatusListener = l;
|
mStatusListener = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combines all of the attributes that have been set on this {@code Builder} and returns a
|
||||||
|
* new {@link AudioPolicy} object.
|
||||||
|
* @return a new {@code AudioPolicy} object.
|
||||||
|
* @throws IllegalStateException if there is no
|
||||||
|
* {@link AudioPolicy.AudioPolicyStatusListener} but the policy was configured
|
||||||
|
* as an audio focus policy with {@link #setIsAudioFocusPolicy(boolean)}.
|
||||||
|
*/
|
||||||
@SystemApi
|
@SystemApi
|
||||||
public AudioPolicy build() {
|
public AudioPolicy build() {
|
||||||
if (mStatusListener != null) {
|
if (mStatusListener != null) {
|
||||||
@ -195,8 +224,12 @@ public class AudioPolicy {
|
|||||||
mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY;
|
mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (mIsFocusPolicy && mFocusListener == null) {
|
||||||
|
throw new IllegalStateException("Cannot be a focus policy without "
|
||||||
|
+ "an AudioPolicyFocusListener");
|
||||||
|
}
|
||||||
return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
|
return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
|
||||||
mFocusListener, mStatusListener);
|
mFocusListener, mStatusListener, mIsFocusPolicy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,6 +435,24 @@ public class AudioPolicy {
|
|||||||
public static abstract class AudioPolicyFocusListener {
|
public static abstract class AudioPolicyFocusListener {
|
||||||
public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {}
|
public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {}
|
||||||
public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {}
|
public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {}
|
||||||
|
/**
|
||||||
|
* Called whenever an application requests audio focus.
|
||||||
|
* Only ever called if the {@link AudioPolicy} was built with
|
||||||
|
* {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
|
||||||
|
* @param afi information about the focus request and the requester
|
||||||
|
* @param requestResult the result that was returned synchronously by the framework to the
|
||||||
|
* application, {@link #AUDIOFOCUS_REQUEST_FAILED},or
|
||||||
|
* {@link #AUDIOFOCUS_REQUEST_DELAYED}.
|
||||||
|
*/
|
||||||
|
public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {}
|
||||||
|
/**
|
||||||
|
* Called whenever an application abandons audio focus.
|
||||||
|
* Only ever called if the {@link AudioPolicy} was built with
|
||||||
|
* {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
|
||||||
|
* @param afi information about the focus request being abandoned and the original
|
||||||
|
* requester.
|
||||||
|
*/
|
||||||
|
public void onAudioFocusAbandon(AudioFocusInfo afi) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPolicyStatusChange() {
|
private void onPolicyStatusChange() {
|
||||||
@ -439,6 +490,22 @@ public class AudioPolicy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void notifyAudioFocusRequest(AudioFocusInfo afi, int requestResult) {
|
||||||
|
sendMsg(MSG_FOCUS_REQUEST, afi, requestResult);
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client="
|
||||||
|
+ afi.getClientId() + "reqRes=" + requestResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notifyAudioFocusAbandon(AudioFocusInfo afi) {
|
||||||
|
sendMsg(MSG_FOCUS_ABANDON, afi, 0 /* ignored */);
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.v(TAG, "notifyAudioFocusAbandon: pack=" + afi.getPackageName() + " client="
|
||||||
|
+ afi.getClientId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void notifyMixStateUpdate(String regId, int state) {
|
public void notifyMixStateUpdate(String regId, int state) {
|
||||||
for (AudioMix mix : mConfig.getMixes()) {
|
for (AudioMix mix : mConfig.getMixes()) {
|
||||||
if (mix.getRegistration().equals(regId)) {
|
if (mix.getRegistration().equals(regId)) {
|
||||||
@ -459,6 +526,8 @@ public class AudioPolicy {
|
|||||||
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 final static int MSG_MIX_STATE_UPDATE = 3;
|
||||||
|
private final static int MSG_FOCUS_REQUEST = 4;
|
||||||
|
private final static int MSG_FOCUS_ABANDON = 5;
|
||||||
|
|
||||||
private class EventHandler extends Handler {
|
private class EventHandler extends Handler {
|
||||||
public EventHandler(AudioPolicy ap, Looper looper) {
|
public EventHandler(AudioPolicy ap, Looper looper) {
|
||||||
@ -488,6 +557,20 @@ public class AudioPolicy {
|
|||||||
mStatusListener.onMixStateUpdate((AudioMix) msg.obj);
|
mStatusListener.onMixStateUpdate((AudioMix) msg.obj);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MSG_FOCUS_REQUEST:
|
||||||
|
if (mFocusListener != null) {
|
||||||
|
mFocusListener.onAudioFocusRequest((AudioFocusInfo) msg.obj, msg.arg1);
|
||||||
|
} else { // should never be null, but don't crash
|
||||||
|
Log.e(TAG, "Invalid null focus listener for focus request event");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MSG_FOCUS_ABANDON:
|
||||||
|
if (mFocusListener != null) { // should never be null
|
||||||
|
mFocusListener.onAudioFocusAbandon((AudioFocusInfo) msg.obj);
|
||||||
|
} else { // should never be null, but don't crash
|
||||||
|
Log.e(TAG, "Invalid null focus listener for focus abandon event");
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
Log.e(TAG, "Unknown event " + msg.what);
|
Log.e(TAG, "Unknown event " + msg.what);
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,12 @@ import android.media.AudioFocusInfo;
|
|||||||
*/
|
*/
|
||||||
oneway interface IAudioPolicyCallback {
|
oneway interface IAudioPolicyCallback {
|
||||||
|
|
||||||
// callbacks for audio focus
|
// callbacks for audio focus listening
|
||||||
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 audio focus policy
|
||||||
|
void notifyAudioFocusRequest(in AudioFocusInfo afi, int requestResult);
|
||||||
|
void notifyAudioFocusAbandon(in AudioFocusInfo afi);
|
||||||
|
|
||||||
// callback for mix activity status update
|
// callback for mix activity status update
|
||||||
void notifyMixStateUpdate(in String regId, int state);
|
void notifyMixStateUpdate(in String regId, int state);
|
||||||
|
@ -55,6 +55,7 @@ import android.hardware.hdmi.HdmiTvClient;
|
|||||||
import android.hardware.usb.UsbManager;
|
import android.hardware.usb.UsbManager;
|
||||||
import android.media.AudioAttributes;
|
import android.media.AudioAttributes;
|
||||||
import android.media.AudioDevicePort;
|
import android.media.AudioDevicePort;
|
||||||
|
import android.media.AudioFocusInfo;
|
||||||
import android.media.AudioSystem;
|
import android.media.AudioSystem;
|
||||||
import android.media.AudioFormat;
|
import android.media.AudioFormat;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
@ -5634,8 +5635,9 @@ public class AudioService extends IAudioService.Stub
|
|||||||
clientId, callingPackageName, flags);
|
clientId, callingPackageName, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa) {
|
public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa,
|
||||||
return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa);
|
String callingPackageName) {
|
||||||
|
return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterAudioFocusClient(String clientId) {
|
public void unregisterAudioFocusClient(String clientId) {
|
||||||
@ -5650,6 +5652,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
return mMediaFocusControl.getFocusRampTimeMs(focusGain, attr);
|
return mMediaFocusControl.getFocusRampTimeMs(focusGain, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================================
|
||||||
private boolean readCameraSoundForced() {
|
private boolean readCameraSoundForced() {
|
||||||
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
|
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
|
||||||
mContext.getResources().getBoolean(
|
mContext.getResources().getBoolean(
|
||||||
@ -6430,7 +6433,7 @@ public class AudioService extends IAudioService.Stub
|
|||||||
// Audio policy management
|
// Audio policy management
|
||||||
//==========================================================================================
|
//==========================================================================================
|
||||||
public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb,
|
public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb,
|
||||||
boolean hasFocusListener) {
|
boolean hasFocusListener, boolean isFocusPolicy) {
|
||||||
AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);
|
AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);
|
||||||
|
|
||||||
if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder()
|
if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder()
|
||||||
@ -6452,7 +6455,8 @@ public class AudioService extends IAudioService.Stub
|
|||||||
Slog.e(TAG, "Cannot re-register policy");
|
Slog.e(TAG, "Cannot re-register policy");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener);
|
AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener,
|
||||||
|
isFocusPolicy);
|
||||||
pcb.asBinder().linkToDeath(app, 0/*flags*/);
|
pcb.asBinder().linkToDeath(app, 0/*flags*/);
|
||||||
regId = app.getRegistrationId();
|
regId = app.getRegistrationId();
|
||||||
mAudioPolicies.put(pcb.asBinder(), app);
|
mAudioPolicies.put(pcb.asBinder(), app);
|
||||||
@ -6650,15 +6654,21 @@ public class AudioService extends IAudioService.Stub
|
|||||||
* is handling ducking for audio focus.
|
* is handling ducking for audio focus.
|
||||||
*/
|
*/
|
||||||
int mFocusDuckBehavior = AudioPolicy.FOCUS_POLICY_DUCKING_DEFAULT;
|
int mFocusDuckBehavior = AudioPolicy.FOCUS_POLICY_DUCKING_DEFAULT;
|
||||||
|
boolean mIsFocusPolicy = false;
|
||||||
|
|
||||||
AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token,
|
AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token,
|
||||||
boolean hasFocusListener) {
|
boolean hasFocusListener, boolean isFocusPolicy) {
|
||||||
super(config);
|
super(config);
|
||||||
setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++));
|
setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++));
|
||||||
mPolicyCallback = token;
|
mPolicyCallback = token;
|
||||||
mHasFocusListener = hasFocusListener;
|
mHasFocusListener = hasFocusListener;
|
||||||
if (mHasFocusListener) {
|
if (mHasFocusListener) {
|
||||||
mMediaFocusControl.addFocusFollower(mPolicyCallback);
|
mMediaFocusControl.addFocusFollower(mPolicyCallback);
|
||||||
|
// can only ever be true if there is a focus listener
|
||||||
|
if (isFocusPolicy) {
|
||||||
|
mIsFocusPolicy = true;
|
||||||
|
mMediaFocusControl.setFocusPolicy(mPolicyCallback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
connectMixes();
|
connectMixes();
|
||||||
}
|
}
|
||||||
@ -6676,6 +6686,9 @@ public class AudioService extends IAudioService.Stub
|
|||||||
}
|
}
|
||||||
|
|
||||||
void release() {
|
void release() {
|
||||||
|
if (mIsFocusPolicy) {
|
||||||
|
mMediaFocusControl.unsetFocusPolicy(mPolicyCallback);
|
||||||
|
}
|
||||||
if (mFocusDuckBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY) {
|
if (mFocusDuckBehavior == AudioPolicy.FOCUS_POLICY_DUCKING_IN_POLICY) {
|
||||||
mMediaFocusControl.setDuckingInExtPolicyAvailable(false);
|
mMediaFocusControl.setDuckingInExtPolicyAvailable(false);
|
||||||
}
|
}
|
||||||
@ -6690,6 +6703,22 @@ public class AudioService extends IAudioService.Stub
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//======================
|
||||||
|
// Audio policy: focus
|
||||||
|
//======================
|
||||||
|
/** */
|
||||||
|
public int dispatchFocusChange(AudioFocusInfo afi, int focusChange, IAudioPolicyCallback pcb) {
|
||||||
|
synchronized (mAudioPolicies) {
|
||||||
|
if (!mAudioPolicies.containsKey(pcb.asBinder())) {
|
||||||
|
throw new IllegalStateException("Unregistered AudioPolicy for focus dispatch");
|
||||||
|
}
|
||||||
|
return mMediaFocusControl.dispatchFocusChange(afi, focusChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================
|
||||||
|
// misc
|
||||||
|
//======================
|
||||||
private HashMap<IBinder, AudioPolicyProxy> mAudioPolicies =
|
private HashMap<IBinder, AudioPolicyProxy> mAudioPolicies =
|
||||||
new HashMap<IBinder, AudioPolicyProxy>();
|
new HashMap<IBinder, AudioPolicyProxy>();
|
||||||
private int mAudioPolicyCounter = 0; // always accessed synchronized on mAudioPolicies
|
private int mAudioPolicyCounter = 0; // always accessed synchronized on mAudioPolicies
|
||||||
|
@ -33,7 +33,7 @@ import java.io.PrintWriter;
|
|||||||
* @hide
|
* @hide
|
||||||
* Class to handle all the information about a user of audio focus. The lifecycle of each
|
* Class to handle all the information about a user of audio focus. The lifecycle of each
|
||||||
* instance is managed by android.media.MediaFocusControl, from its addition to the audio focus
|
* instance is managed by android.media.MediaFocusControl, from its addition to the audio focus
|
||||||
* stack to its release.
|
* stack, or the map of focus owners for an external focus policy, to its release.
|
||||||
*/
|
*/
|
||||||
public class FocusRequester {
|
public class FocusRequester {
|
||||||
|
|
||||||
@ -101,6 +101,21 @@ public class FocusRequester {
|
|||||||
mFocusController = ctlr;
|
mFocusController = ctlr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FocusRequester(AudioFocusInfo afi, IAudioFocusDispatcher afl,
|
||||||
|
IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr) {
|
||||||
|
mAttributes = afi.getAttributes();
|
||||||
|
mClientId = afi.getClientId();
|
||||||
|
mPackageName = afi.getPackageName();
|
||||||
|
mCallingUid = afi.getClientUid();
|
||||||
|
mFocusGainRequest = afi.getGainRequest();
|
||||||
|
mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
|
||||||
|
mGrantFlags = afi.getFlags();
|
||||||
|
|
||||||
|
mFocusDispatcher = afl;
|
||||||
|
mSourceRef = source;
|
||||||
|
mDeathHandler = hdlr;
|
||||||
|
mFocusController = ctlr;
|
||||||
|
}
|
||||||
|
|
||||||
boolean hasSameClient(String otherClient) {
|
boolean hasSameClient(String otherClient) {
|
||||||
try {
|
try {
|
||||||
@ -118,6 +133,10 @@ public class FocusRequester {
|
|||||||
return (mSourceRef != null) && mSourceRef.equals(ib);
|
return (mSourceRef != null) && mSourceRef.equals(ib);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasSameDispatcher(IAudioFocusDispatcher fd) {
|
||||||
|
return (mFocusDispatcher != null) && mFocusDispatcher.equals(fd);
|
||||||
|
}
|
||||||
|
|
||||||
boolean hasSamePackage(String pack) {
|
boolean hasSamePackage(String pack) {
|
||||||
try {
|
try {
|
||||||
return mPackageName.compareTo(pack) == 0;
|
return mPackageName.compareTo(pack) == 0;
|
||||||
@ -369,6 +388,35 @@ public class FocusRequester {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int dispatchFocusChange(int focusChange) {
|
||||||
|
if (mFocusDispatcher == null) {
|
||||||
|
if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: no focus dispatcher"); }
|
||||||
|
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
|
||||||
|
}
|
||||||
|
if (focusChange == AudioManager.AUDIOFOCUS_NONE) {
|
||||||
|
if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: AUDIOFOCUS_NONE"); }
|
||||||
|
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
|
||||||
|
} else if ((focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
|
||||||
|
|| focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
|
||||||
|
|| focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
|
||||||
|
|| focusChange == AudioManager.AUDIOFOCUS_GAIN)
|
||||||
|
&& (mFocusGainRequest != focusChange)){
|
||||||
|
Log.w(TAG, "focus gain was requested with " + mFocusGainRequest
|
||||||
|
+ ", dispatching " + focusChange);
|
||||||
|
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
|
||||||
|
|| focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
|
||||||
|
|| focusChange == AudioManager.AUDIOFOCUS_LOSS) {
|
||||||
|
mFocusLossReceived = focusChange;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mFocusDispatcher.dispatchAudioFocusChange(focusChange, mClientId);
|
||||||
|
} catch (android.os.RemoteException e) {
|
||||||
|
Log.v(TAG, "dispatchFocusChange: error talking to focus listener", e);
|
||||||
|
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
|
||||||
|
}
|
||||||
|
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
|
||||||
|
}
|
||||||
|
|
||||||
AudioFocusInfo toAudioFocusInfo() {
|
AudioFocusInfo toAudioFocusInfo() {
|
||||||
return new AudioFocusInfo(mAttributes, mCallingUid, mClientId, mPackageName,
|
return new AudioFocusInfo(mAttributes, mCallingUid, mClientId, mPackageName,
|
||||||
mFocusGainRequest, mFocusLossReceived, mGrantFlags);
|
mFocusGainRequest, mFocusLossReceived, mGrantFlags);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.server.audio;
|
package com.android.server.audio;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.AudioAttributes;
|
import android.media.AudioAttributes;
|
||||||
@ -23,6 +24,7 @@ import android.media.AudioFocusInfo;
|
|||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.media.AudioSystem;
|
import android.media.AudioSystem;
|
||||||
import android.media.IAudioFocusDispatcher;
|
import android.media.IAudioFocusDispatcher;
|
||||||
|
import android.media.audiopolicy.AudioPolicy;
|
||||||
import android.media.audiopolicy.IAudioPolicyCallback;
|
import android.media.audiopolicy.IAudioPolicyCallback;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@ -32,7 +34,10 @@ import android.util.Log;
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
|
||||||
@ -43,6 +48,7 @@ import java.text.DateFormat;
|
|||||||
public class MediaFocusControl implements PlayerFocusEnforcer {
|
public class MediaFocusControl implements PlayerFocusEnforcer {
|
||||||
|
|
||||||
private static final String TAG = "MediaFocusControl";
|
private static final String TAG = "MediaFocusControl";
|
||||||
|
static final boolean DEBUG = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set to true so the framework enforces ducking itself, without communicating to apps
|
* set to true so the framework enforces ducking itself, without communicating to apps
|
||||||
@ -155,6 +161,13 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
|||||||
while(stackIterator.hasNext()) {
|
while(stackIterator.hasNext()) {
|
||||||
stackIterator.next().dump(pw);
|
stackIterator.next().dump(pw);
|
||||||
}
|
}
|
||||||
|
pw.println("\n");
|
||||||
|
if (mFocusPolicy == null) {
|
||||||
|
pw.println("No external focus policy\n");
|
||||||
|
} else {
|
||||||
|
pw.println("External focus policy: "+ mFocusPolicy + ", focus owners:\n");
|
||||||
|
dumpExtFocusPolicyFocusOwners(pw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pw.println("\n");
|
pw.println("\n");
|
||||||
pw.println(" Notify on duck: " + mNotifyFocusOwnerOnDuck + "\n");
|
pw.println(" Notify on duck: " + mNotifyFocusOwnerOnDuck + "\n");
|
||||||
@ -233,6 +246,31 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for external focus policy:
|
||||||
|
* Called synchronized on mAudioFocusLock
|
||||||
|
* Remove focus listeners from the list of potential focus owners for a particular client when
|
||||||
|
* it has died.
|
||||||
|
*/
|
||||||
|
private void removeFocusEntryForExtPolicy(IBinder cb) {
|
||||||
|
if (mFocusOwnersForFocusPolicy.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean released = false;
|
||||||
|
final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
|
||||||
|
final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
|
||||||
|
while (ownerIterator.hasNext()) {
|
||||||
|
final Entry<String, FocusRequester> owner = ownerIterator.next();
|
||||||
|
final FocusRequester fr = owner.getValue();
|
||||||
|
if (fr.hasSameBinder(cb)) {
|
||||||
|
ownerIterator.remove();
|
||||||
|
fr.release();
|
||||||
|
notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function:
|
* Helper function:
|
||||||
* Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
|
* Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
|
||||||
@ -297,10 +335,14 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
|||||||
|
|
||||||
public void binderDied() {
|
public void binderDied() {
|
||||||
synchronized(mAudioFocusLock) {
|
synchronized(mAudioFocusLock) {
|
||||||
|
if (mFocusPolicy != null) {
|
||||||
|
removeFocusEntryForExtPolicy(mCb);
|
||||||
|
} else {
|
||||||
removeFocusStackEntryOnDeath(mCb);
|
removeFocusStackEntryOnDeath(mCb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether to notify an audio focus owner when it loses focus
|
* Indicates whether to notify an audio focus owner when it loses focus
|
||||||
@ -353,6 +395,34 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IAudioPolicyCallback mFocusPolicy = null;
|
||||||
|
|
||||||
|
// Since we don't have a stack of focus owners when using an external focus policy, we keep
|
||||||
|
// track of all the focus requesters in this map, with their clientId as the key. This is
|
||||||
|
// used both for focus dispatch and death handling
|
||||||
|
private HashMap<String, FocusRequester> mFocusOwnersForFocusPolicy =
|
||||||
|
new HashMap<String, FocusRequester>();
|
||||||
|
|
||||||
|
void setFocusPolicy(IAudioPolicyCallback policy) {
|
||||||
|
if (policy == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (mAudioFocusLock) {
|
||||||
|
mFocusPolicy = policy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unsetFocusPolicy(IAudioPolicyCallback policy) {
|
||||||
|
if (policy == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (mAudioFocusLock) {
|
||||||
|
if (mFocusPolicy == policy) {
|
||||||
|
mFocusPolicy = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param pcb non null
|
* @param pcb non null
|
||||||
*/
|
*/
|
||||||
@ -409,6 +479,100 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called synchronized on mAudioFocusLock
|
||||||
|
* @param afi
|
||||||
|
* @param requestResult
|
||||||
|
* @return true if the external audio focus policy (if any) is handling the focus request
|
||||||
|
*/
|
||||||
|
boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, int requestResult,
|
||||||
|
IAudioFocusDispatcher fd, IBinder cb) {
|
||||||
|
if (mFocusPolicy == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId()
|
||||||
|
+ " dispatcher=" + fd);
|
||||||
|
}
|
||||||
|
final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
|
||||||
|
if (existingFr != null) {
|
||||||
|
if (!existingFr.hasSameDispatcher(fd)) {
|
||||||
|
existingFr.release();
|
||||||
|
final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb);
|
||||||
|
mFocusOwnersForFocusPolicy.put(afi.getClientId(),
|
||||||
|
new FocusRequester(afi, fd, cb, hdlr, this));
|
||||||
|
}
|
||||||
|
} else if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
|
||||||
|
|| requestResult == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
|
||||||
|
// new focus (future) focus owner to keep track of
|
||||||
|
final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb);
|
||||||
|
mFocusOwnersForFocusPolicy.put(afi.getClientId(),
|
||||||
|
new FocusRequester(afi, fd, cb, hdlr, this));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
//oneway
|
||||||
|
mFocusPolicy.notifyAudioFocusRequest(afi, requestResult);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback "
|
||||||
|
+ mFocusPolicy.asBinder(), e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called synchronized on mAudioFocusLock
|
||||||
|
* @param afi
|
||||||
|
* @param requestResult
|
||||||
|
* @return true if the external audio focus policy (if any) is handling the focus request
|
||||||
|
*/
|
||||||
|
boolean notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi) {
|
||||||
|
if (mFocusPolicy == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final FocusRequester fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
|
||||||
|
if (fr != null) {
|
||||||
|
fr.release();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
//oneway
|
||||||
|
mFocusPolicy.notifyAudioFocusAbandon(afi);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Can't call notifyAudioFocusAbandon() on IAudioPolicyCallback "
|
||||||
|
+ mFocusPolicy.asBinder(), e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** see AudioManager.dispatchFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) */
|
||||||
|
int dispatchFocusChange(AudioFocusInfo afi, int focusChange) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.v(TAG, "dispatchFocusChange " + focusChange + " to afi client="
|
||||||
|
+ afi.getClientId());
|
||||||
|
}
|
||||||
|
synchronized (mAudioFocusLock) {
|
||||||
|
if (mFocusPolicy == null) {
|
||||||
|
if (DEBUG) { Log.v(TAG, "> failed: no focus policy" ); }
|
||||||
|
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
|
||||||
|
}
|
||||||
|
final FocusRequester fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
|
||||||
|
if (fr == null) {
|
||||||
|
if (DEBUG) { Log.v(TAG, "> failed: no such focus requester known" ); }
|
||||||
|
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
|
||||||
|
}
|
||||||
|
return fr.dispatchFocusChange(focusChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) {
|
||||||
|
final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
|
||||||
|
final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
|
||||||
|
while (ownerIterator.hasNext()) {
|
||||||
|
final Entry<String, FocusRequester> owner = ownerIterator.next();
|
||||||
|
final FocusRequester fr = owner.getValue();
|
||||||
|
fr.dump(pw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected int getCurrentAudioFocus() {
|
protected int getCurrentAudioFocus() {
|
||||||
synchronized(mAudioFocusLock) {
|
synchronized(mAudioFocusLock) {
|
||||||
if (mFocusStack.empty()) {
|
if (mFocusStack.empty()) {
|
||||||
@ -487,10 +651,23 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
|||||||
& (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
|
& (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
|
||||||
if (enteringRingOrCall) { mRingOrCallActive = true; }
|
if (enteringRingOrCall) { mRingOrCallActive = true; }
|
||||||
|
|
||||||
|
final AudioFocusInfo afiForExtPolicy;
|
||||||
|
if (mFocusPolicy != null) {
|
||||||
|
// construct AudioFocusInfo as it will be communicated to audio focus policy
|
||||||
|
afiForExtPolicy = new AudioFocusInfo(aa, Binder.getCallingUid(),
|
||||||
|
clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/,
|
||||||
|
flags);
|
||||||
|
} else {
|
||||||
|
afiForExtPolicy = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle delayed focus
|
||||||
boolean focusGrantDelayed = false;
|
boolean focusGrantDelayed = false;
|
||||||
if (!canReassignAudioFocus()) {
|
if (!canReassignAudioFocus()) {
|
||||||
if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
|
if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
|
||||||
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
|
final int result = AudioManager.AUDIOFOCUS_REQUEST_FAILED;
|
||||||
|
notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, result, fd, cb);
|
||||||
|
return result;
|
||||||
} else {
|
} else {
|
||||||
// request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be
|
// request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be
|
||||||
// granted right now, so the requester will be inserted in the focus stack
|
// granted right now, so the requester will be inserted in the focus stack
|
||||||
@ -499,6 +676,14 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// external focus policy: delay request for focus gain?
|
||||||
|
final int resultWithExtPolicy = AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
|
||||||
|
if (notifyExtFocusPolicyFocusRequest_syncAf(
|
||||||
|
afiForExtPolicy, resultWithExtPolicy, fd, cb)) {
|
||||||
|
// stop handling focus request here as it is handled by external audio focus policy
|
||||||
|
return resultWithExtPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
// handle the potential premature death of the new holder of the focus
|
// handle the potential premature death of the new holder of the focus
|
||||||
// (premature death == death before abandoning focus)
|
// (premature death == death before abandoning focus)
|
||||||
// Register for client death notification
|
// Register for client death notification
|
||||||
@ -569,7 +754,8 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
|||||||
/**
|
/**
|
||||||
* @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
|
* @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
|
||||||
* */
|
* */
|
||||||
protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa) {
|
protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa,
|
||||||
|
String callingPackageName) {
|
||||||
// AudioAttributes are currently ignored, to be used for zones
|
// AudioAttributes are currently ignored, to be used for zones
|
||||||
Log.i(TAG, " AudioFocus abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
|
Log.i(TAG, " AudioFocus abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
|
||||||
+ "/" + Binder.getCallingPid()
|
+ "/" + Binder.getCallingPid()
|
||||||
@ -577,6 +763,16 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
|||||||
try {
|
try {
|
||||||
// this will take care of notifying the new focus owner if needed
|
// this will take care of notifying the new focus owner if needed
|
||||||
synchronized(mAudioFocusLock) {
|
synchronized(mAudioFocusLock) {
|
||||||
|
// external focus policy?
|
||||||
|
if (mFocusPolicy != null) {
|
||||||
|
final AudioFocusInfo afi = new AudioFocusInfo(aa, Binder.getCallingUid(),
|
||||||
|
clientId, callingPackageName, 0 /*gainRequest*/, 0 /*lossReceived*/,
|
||||||
|
0 /*flags*/);
|
||||||
|
if (notifyExtFocusPolicyFocusAbandon_syncAf(afi)) {
|
||||||
|
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boolean exitingRingOrCall = mRingOrCallActive
|
boolean exitingRingOrCall = mRingOrCallActive
|
||||||
& (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
|
& (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
|
||||||
if (exitingRingOrCall) { mRingOrCallActive = false; }
|
if (exitingRingOrCall) { mRingOrCallActive = false; }
|
||||||
|
Reference in New Issue
Block a user