Fields Summary |
---|
private static final String | TAG |
private static final boolean | DEBUG |
private final Object | mLock |
public static final int | POLICY_STATUS_UNREGISTEREDThe status of an audio policy that is valid but cannot be used because it is not registered. |
public static final int | POLICY_STATUS_REGISTEREDThe status of an audio policy that is valid, successfully registered and thus active. |
private int | mStatus |
private String | mRegistrationId |
private AudioPolicyStatusListener | mStatusListener |
public static final int | FOCUS_POLICY_DUCKING_IN_APPThe behavior of a policy with regards to audio focus where it relies on the application
to do the ducking, the is the legacy and default behavior. |
public static final int | FOCUS_POLICY_DUCKING_DEFAULT |
public static final int | FOCUS_POLICY_DUCKING_IN_POLICYThe behavior of a policy with regards to audio focus where it handles ducking instead
of the application losing focus and being signaled it can duck (as communicated by
{@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}).
Can only be used after having set a listener with
{@link AudioPolicy#setAudioPolicyFocusListener(AudioPolicyFocusListener)}. |
private AudioPolicyFocusListener | mFocusListener |
private android.content.Context | mContext |
private AudioPolicyConfig | mConfig |
private final IAudioPolicyCallback | mPolicyCb |
private final EventHandler | mEventHandler |
private static final int | MSG_POLICY_STATUS_CHANGE |
private static final int | MSG_FOCUS_GRANT |
private static final int | MSG_FOCUS_LOSS |
private static android.media.IAudioService | sService |
Methods Summary |
---|
private static java.lang.String | addressForTag(AudioMix mix)
return "addr=" + mix.getRegistration();
|
public IAudioPolicyCallback | cb() return mPolicyCb;
|
private void | checkMixReadyToUse(AudioMix mix, boolean forTrack)
if (mix == null) {
String msg = forTrack ? "Invalid null AudioMix for AudioTrack creation"
: "Invalid null AudioMix for AudioRecord creation";
throw new IllegalArgumentException(msg);
}
if (!mConfig.mMixes.contains(mix)) {
throw new IllegalArgumentException("Invalid mix: not part of this policy");
}
if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) != AudioMix.ROUTE_FLAG_LOOP_BACK)
{
throw new IllegalArgumentException("Invalid AudioMix: not defined for loop back");
}
if (forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_RECORDERS)) {
throw new IllegalArgumentException(
"Invalid AudioMix: not defined for being a recording source");
}
if (!forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_PLAYERS)) {
throw new IllegalArgumentException(
"Invalid AudioMix: not defined for capturing playback");
}
|
public android.media.AudioRecord | createAudioRecordSink(AudioMix mix)Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}.
Audio buffers recorded through the created instance will contain the mix of the audio
streams that fed the given mixer.
if (!policyReadyToUse()) {
Log.e(TAG, "Cannot create AudioRecord sink for AudioMix");
return null;
}
checkMixReadyToUse(mix, false/*not for an AudioTrack*/);
// create an AudioFormat from the mix format compatible with recording, as the mix
// was defined for playback
AudioFormat mixFormat = new AudioFormat.Builder(mix.getFormat())
.setChannelMask(AudioFormat.inChannelMaskFromOutChannelMask(
mix.getFormat().getChannelMask()))
.build();
// create the AudioRecord, configured for loop back, using the same format as the mix
AudioRecord ar = new AudioRecord(
new AudioAttributes.Builder()
.setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX)
.addTag(addressForTag(mix))
.build(),
mixFormat,
AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(),
// using stereo for buffer size to avoid the current poor support for masks
AudioFormat.CHANNEL_IN_STEREO, mix.getFormat().getEncoding()),
AudioManager.AUDIO_SESSION_ID_GENERATE
);
return ar;
|
public android.media.AudioTrack | createAudioTrackSource(AudioMix mix)Create an {@link AudioTrack} instance that is associated with the given {@link AudioMix}.
Audio buffers played through the created instance will be sent to the given mix
to be recorded through the recording APIs.
if (!policyReadyToUse()) {
Log.e(TAG, "Cannot create AudioTrack source for AudioMix");
return null;
}
checkMixReadyToUse(mix, true/*for an AudioTrack*/);
// create the AudioTrack, configured for loop back, using the same format as the mix
AudioTrack at = new AudioTrack(
new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_VIRTUAL_SOURCE)
.addTag(addressForTag(mix))
.build(),
mix.getFormat(),
AudioTrack.getMinBufferSize(mix.getFormat().getSampleRate(),
mix.getFormat().getChannelMask(), mix.getFormat().getEncoding()),
AudioTrack.MODE_STREAM,
AudioManager.AUDIO_SESSION_ID_GENERATE
);
return at;
|
public AudioPolicyConfig | getConfig()
return mConfig;
|
public int | getFocusDuckingBehavior()Returns the current behavior for audio focus-related ducking.
return mConfig.mDuckingPolicy;
|
private static android.media.IAudioService | getService()
if (sService != null) {
return sService;
}
IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
sService = IAudioService.Stub.asInterface(b);
return sService;
|
public int | getStatus()
return mStatus;
|
public boolean | hasFocusListener() return mFocusListener != null;
|
private void | onPolicyStatusChange()
AudioPolicyStatusListener l;
synchronized (mLock) {
if (mStatusListener == null) {
return;
}
l = mStatusListener;
}
l.onStatusChange();
|
private boolean | policyReadyToUse()
synchronized (mLock) {
if (mStatus != POLICY_STATUS_REGISTERED) {
Log.e(TAG, "Cannot use unregistered AudioPolicy");
return false;
}
if (mContext == null) {
Log.e(TAG, "Cannot use AudioPolicy without context");
return false;
}
if (mRegistrationId == null) {
Log.e(TAG, "Cannot use unregistered AudioPolicy");
return false;
}
}
if (!(PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
Slog.w(TAG, "Cannot use AudioPolicy for pid " + Binder.getCallingPid() + " / uid "
+ Binder.getCallingUid() + ", needs MODIFY_AUDIO_ROUTING");
return false;
}
return true;
|
private void | sendMsg(int msg)
if (mEventHandler != null) {
mEventHandler.sendEmptyMessage(msg);
}
|
private void | sendMsg(int msg, java.lang.Object obj, int i)
if (mEventHandler != null) {
mEventHandler.sendMessage(
mEventHandler.obtainMessage(msg, i /*arg1*/, 0 /*arg2, ignored*/, obj));
}
|
public int | setFocusDuckingBehavior(int behavior)Sets the behavior for audio focus-related ducking.
There must be a focus listener if this policy is to handle ducking.
if ((behavior != FOCUS_POLICY_DUCKING_IN_APP)
&& (behavior != FOCUS_POLICY_DUCKING_IN_POLICY)) {
throw new IllegalArgumentException("Invalid ducking behavior " + behavior);
}
synchronized (mLock) {
if (mStatus != POLICY_STATUS_REGISTERED) {
throw new IllegalStateException(
"Cannot change ducking behavior for unregistered policy");
}
if ((behavior == FOCUS_POLICY_DUCKING_IN_POLICY)
&& (mFocusListener == null)) {
// there must be a focus listener if the policy handles ducking
throw new IllegalStateException(
"Cannot handle ducking without an audio focus listener");
}
IAudioService service = getService();
try {
final int status = service.setFocusPropertiesForPolicy(behavior /*duckingBehavior*/,
this.cb());
if (status == AudioManager.SUCCESS) {
mConfig.mDuckingPolicy = behavior;
}
return status;
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setFocusPropertiesForPolicy for behavior", e);
return AudioManager.ERROR;
}
}
|
public void | setRegistration(java.lang.String regId)
synchronized (mLock) {
mRegistrationId = regId;
mConfig.setRegistration(regId);
if (regId != null) {
mStatus = POLICY_STATUS_REGISTERED;
} else {
mStatus = POLICY_STATUS_UNREGISTERED;
}
}
sendMsg(MSG_POLICY_STATUS_CHANGE);
|
public java.lang.String | toLogFriendlyString()
String textDump = new String("android.media.audiopolicy.AudioPolicy:\n");
textDump += "config=" + mConfig.toLogFriendlyString();
return (textDump);
|