Merge "More AudioPolicy registration" into lmp-mr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
38b00a63c9
@@ -161,6 +161,12 @@ public final class AudioAttributes implements Parcelable {
|
|||||||
* Usage value to use when the usage is for game audio.
|
* Usage value to use when the usage is for game audio.
|
||||||
*/
|
*/
|
||||||
public final static int USAGE_GAME = 14;
|
public final static int USAGE_GAME = 14;
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
* Usage value to use when feeding audio to the platform and replacing "traditional" audio
|
||||||
|
* source, such as audio capture devices.
|
||||||
|
*/
|
||||||
|
public final static int USAGE_VIRTUAL_SOURCE = 15;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag defining a behavior where the audibility of the sound will be ensured by the system.
|
* Flag defining a behavior where the audibility of the sound will be ensured by the system.
|
||||||
@@ -374,6 +380,7 @@ public final class AudioAttributes implements Parcelable {
|
|||||||
case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
|
case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
|
||||||
case USAGE_ASSISTANCE_SONIFICATION:
|
case USAGE_ASSISTANCE_SONIFICATION:
|
||||||
case USAGE_GAME:
|
case USAGE_GAME:
|
||||||
|
case USAGE_VIRTUAL_SOURCE:
|
||||||
mUsage = usage;
|
mUsage = usage;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -2663,9 +2663,13 @@ public class AudioManager {
|
|||||||
}
|
}
|
||||||
IAudioService service = getService();
|
IAudioService service = getService();
|
||||||
try {
|
try {
|
||||||
if (!service.registerAudioPolicy(policy.getConfig(), policy.token())) {
|
String regId = service.registerAudioPolicy(policy.getConfig(), policy.token());
|
||||||
|
if (regId == null) {
|
||||||
return ERROR;
|
return ERROR;
|
||||||
|
} else {
|
||||||
|
policy.setRegistration(regId);
|
||||||
}
|
}
|
||||||
|
// successful registration
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Dead object in registerAudioPolicyAsync()", e);
|
Log.e(TAG, "Dead object in registerAudioPolicyAsync()", e);
|
||||||
return ERROR;
|
return ERROR;
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import android.hardware.hdmi.HdmiTvClient;
|
|||||||
import android.hardware.usb.UsbManager;
|
import android.hardware.usb.UsbManager;
|
||||||
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.AudioPolicyConfig;
|
import android.media.audiopolicy.AudioPolicyConfig;
|
||||||
import android.media.session.MediaSessionLegacyHelper;
|
import android.media.session.MediaSessionLegacyHelper;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
@@ -118,6 +119,10 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
|
|
||||||
/** Debug audio mode */
|
/** Debug audio mode */
|
||||||
protected static final boolean DEBUG_MODE = Log.isLoggable(TAG + ".MOD", Log.DEBUG);
|
protected static final boolean DEBUG_MODE = Log.isLoggable(TAG + ".MOD", Log.DEBUG);
|
||||||
|
|
||||||
|
/** Debug audio policy feature */
|
||||||
|
protected static final boolean DEBUG_AP = Log.isLoggable(TAG + ".AP", Log.DEBUG);
|
||||||
|
|
||||||
/** Debug volumes */
|
/** Debug volumes */
|
||||||
protected static final boolean DEBUG_VOL = Log.isLoggable(TAG + ".VOL", Log.DEBUG);
|
protected static final boolean DEBUG_VOL = Log.isLoggable(TAG + ".VOL", Log.DEBUG);
|
||||||
|
|
||||||
@@ -5634,31 +5639,33 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
//==========================================================================================
|
//==========================================================================================
|
||||||
// Audio policy management
|
// Audio policy management
|
||||||
//==========================================================================================
|
//==========================================================================================
|
||||||
public boolean registerAudioPolicy(AudioPolicyConfig policyConfig, IBinder cb) {
|
public String registerAudioPolicy(AudioPolicyConfig policyConfig, IBinder cb) {
|
||||||
//Log.v(TAG, "registerAudioPolicy for " + cb + " got policy:" + policyConfig);
|
//Log.v(TAG, "registerAudioPolicy for " + cb + " got policy:" + policyConfig);
|
||||||
|
String regId = null;
|
||||||
boolean hasPermissionForPolicy =
|
boolean hasPermissionForPolicy =
|
||||||
(PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
|
(PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
|
||||||
android.Manifest.permission.MODIFY_AUDIO_ROUTING));
|
android.Manifest.permission.MODIFY_AUDIO_ROUTING));
|
||||||
if (!hasPermissionForPolicy) {
|
if (!hasPermissionForPolicy) {
|
||||||
Slog.w(TAG, "Can't register audio policy for pid " + Binder.getCallingPid() + " / uid "
|
Slog.w(TAG, "Can't register audio policy for pid " + Binder.getCallingPid() + " / uid "
|
||||||
+ Binder.getCallingUid() + ", need MODIFY_AUDIO_ROUTING");
|
+ Binder.getCallingUid() + ", need MODIFY_AUDIO_ROUTING");
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
synchronized (mAudioPolicies) {
|
synchronized (mAudioPolicies) {
|
||||||
AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, cb);
|
|
||||||
try {
|
try {
|
||||||
|
AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, cb);
|
||||||
cb.linkToDeath(app, 0/*flags*/);
|
cb.linkToDeath(app, 0/*flags*/);
|
||||||
|
regId = app.connectMixes();
|
||||||
mAudioPolicies.put(cb, app);
|
mAudioPolicies.put(cb, app);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
// audio policy owner has already died!
|
// audio policy owner has already died!
|
||||||
Slog.w(TAG, "Audio policy registration failed, could not link to " + cb +
|
Slog.w(TAG, "Audio policy registration failed, could not link to " + cb +
|
||||||
" binder death", e);
|
" binder death", e);
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO implement registration with native audio policy (including permission check)
|
return regId;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterAudioPolicyAsync(IBinder cb) {
|
public void unregisterAudioPolicyAsync(IBinder cb) {
|
||||||
synchronized (mAudioPolicies) {
|
synchronized (mAudioPolicies) {
|
||||||
AudioPolicyProxy app = mAudioPolicies.remove(cb);
|
AudioPolicyProxy app = mAudioPolicies.remove(cb);
|
||||||
@@ -5668,27 +5675,59 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
} else {
|
} else {
|
||||||
cb.unlinkToDeath(app, 0/*flags*/);
|
cb.unlinkToDeath(app, 0/*flags*/);
|
||||||
}
|
}
|
||||||
|
app.disconnectMixes();
|
||||||
}
|
}
|
||||||
// TODO implement registration with native audio policy
|
// TODO implement clearing mix attribute matching info in native audio policy
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AudioPolicyProxy implements IBinder.DeathRecipient {
|
/**
|
||||||
|
* This internal class inherits from AudioPolicyConfig which contains all the mixes and
|
||||||
|
* their configurations.
|
||||||
|
*/
|
||||||
|
public class AudioPolicyProxy extends AudioPolicyConfig implements IBinder.DeathRecipient {
|
||||||
private static final String TAG = "AudioPolicyProxy";
|
private static final String TAG = "AudioPolicyProxy";
|
||||||
AudioPolicyConfig mConfig;
|
AudioPolicyConfig mConfig;
|
||||||
IBinder mToken;
|
IBinder mToken;
|
||||||
AudioPolicyProxy(AudioPolicyConfig config, IBinder token) {
|
AudioPolicyProxy(AudioPolicyConfig config, IBinder token) {
|
||||||
mConfig = config;
|
super(config);
|
||||||
|
setRegistration(new String(config.toString() + ":ap:" + mAudioPolicyCounter++));
|
||||||
mToken = token;
|
mToken = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void binderDied() {
|
public void binderDied() {
|
||||||
synchronized (mAudioPolicies) {
|
synchronized (mAudioPolicies) {
|
||||||
Log.v(TAG, "audio policy " + mToken + " died");
|
Log.i(TAG, "audio policy " + mToken + " died");
|
||||||
mAudioPolicies.remove(mToken);
|
mAudioPolicies.remove(mToken);
|
||||||
|
disconnectMixes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String connectMixes() {
|
||||||
|
updateMixes(AudioSystem.DEVICE_STATE_AVAILABLE);
|
||||||
|
return mRegistrationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disconnectMixes() {
|
||||||
|
updateMixes(AudioSystem.DEVICE_STATE_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateMixes(int connectionState) {
|
||||||
|
for (AudioMix mix : mMixes) {
|
||||||
|
// TODO implement sending the mix attribute matching info to native audio policy
|
||||||
|
if (DEBUG_AP) {
|
||||||
|
Log.v(TAG, "AudioPolicyProxy connect mix state=" + connectionState
|
||||||
|
+ " addr=" + mix.getRegistration()); }
|
||||||
|
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_REMOTE_SUBMIX,
|
||||||
|
connectionState,
|
||||||
|
mix.getRegistration());
|
||||||
|
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX,
|
||||||
|
connectionState,
|
||||||
|
mix.getRegistration());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,6 +207,6 @@ interface IAudioService {
|
|||||||
|
|
||||||
boolean isHdmiSystemAudioSupported();
|
boolean isHdmiSystemAudioSupported();
|
||||||
|
|
||||||
boolean registerAudioPolicy(in AudioPolicyConfig policyConfig, IBinder cb);
|
String registerAudioPolicy(in AudioPolicyConfig policyConfig, IBinder cb);
|
||||||
oneway void unregisterAudioPolicyAsync(in IBinder cb);
|
oneway void unregisterAudioPolicyAsync(in IBinder cb);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,14 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide CANDIDATE FOR PUBLIC API
|
* @hide
|
||||||
*/
|
*/
|
||||||
public class AudioMix {
|
public class AudioMix {
|
||||||
|
|
||||||
private AudioMixingRule mRule;
|
private AudioMixingRule mRule;
|
||||||
private AudioFormat mFormat;
|
private AudioFormat mFormat;
|
||||||
private int mRouteFlags;
|
private int mRouteFlags;
|
||||||
|
private String mRegistrationId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All parameters are guaranteed valid through the Builder.
|
* All parameters are guaranteed valid through the Builder.
|
||||||
@@ -39,6 +40,7 @@ public class AudioMix {
|
|||||||
mRule = rule;
|
mRule = rule;
|
||||||
mFormat = format;
|
mFormat = format;
|
||||||
mRouteFlags = routeFlags;
|
mRouteFlags = routeFlags;
|
||||||
|
mRegistrationId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,6 +67,15 @@ public class AudioMix {
|
|||||||
return mRule;
|
return mRule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setRegistration(String regId) {
|
||||||
|
mRegistrationId = regId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public String getRegistration() {
|
||||||
|
return mRegistrationId;
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@IntDef(flag = true,
|
@IntDef(flag = true,
|
||||||
value = { ROUTE_FLAG_RENDER, ROUTE_FLAG_LOOP_BACK } )
|
value = { ROUTE_FLAG_RENDER, ROUTE_FLAG_LOOP_BACK } )
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import java.util.Iterator;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide CANDIDATE FOR PUBLIC API
|
* @hide
|
||||||
*
|
*
|
||||||
* Here's an example of creating a mixing rule for all media playback:
|
* Here's an example of creating a mixing rule for all media playback:
|
||||||
* <pre>
|
* <pre>
|
||||||
|
|||||||
@@ -17,18 +17,26 @@
|
|||||||
package android.media.audiopolicy;
|
package android.media.audiopolicy;
|
||||||
|
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.media.AudioAttributes;
|
||||||
import android.media.AudioFormat;
|
import android.media.AudioFormat;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
|
import android.media.AudioRecord;
|
||||||
|
import android.media.AudioSystem;
|
||||||
|
import android.media.AudioTrack;
|
||||||
|
import android.media.MediaRecorder;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Slog;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide CANDIDATE FOR PUBLIC API
|
* @hide
|
||||||
* AudioPolicy provides access to the management of audio routing and audio focus.
|
* AudioPolicy provides access to the management of audio routing and audio focus.
|
||||||
*/
|
*/
|
||||||
public class AudioPolicy {
|
public class AudioPolicy {
|
||||||
@@ -49,11 +57,13 @@ public class AudioPolicy {
|
|||||||
public static final int POLICY_STATUS_REGISTERED = 2;
|
public static final int POLICY_STATUS_REGISTERED = 2;
|
||||||
|
|
||||||
private int mStatus;
|
private int mStatus;
|
||||||
private AudioPolicyStatusListener mStatusListener = null;
|
private String mRegistrationId;
|
||||||
|
private AudioPolicyStatusListener mStatusListener;
|
||||||
|
|
||||||
private final IBinder mToken = new Binder();
|
private final IBinder mToken = new Binder();
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public IBinder token() { return mToken; }
|
public IBinder token() { return mToken; }
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
private AudioPolicyConfig mConfig;
|
private AudioPolicyConfig mConfig;
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@@ -62,13 +72,14 @@ public class AudioPolicy {
|
|||||||
/**
|
/**
|
||||||
* The parameter is guaranteed non-null through the Builder
|
* The parameter is guaranteed non-null through the Builder
|
||||||
*/
|
*/
|
||||||
private AudioPolicy(AudioPolicyConfig config) {
|
private AudioPolicy(AudioPolicyConfig config, Context context) {
|
||||||
mConfig = config;
|
mConfig = config;
|
||||||
if (mConfig.mMixes.isEmpty()) {
|
if (mConfig.mMixes.isEmpty()) {
|
||||||
mStatus = POLICY_STATUS_INVALID;
|
mStatus = POLICY_STATUS_INVALID;
|
||||||
} else {
|
} else {
|
||||||
mStatus = POLICY_STATUS_UNREGISTERED;
|
mStatus = POLICY_STATUS_UNREGISTERED;
|
||||||
}
|
}
|
||||||
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,12 +87,15 @@ public class AudioPolicy {
|
|||||||
*/
|
*/
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private ArrayList<AudioMix> mMixes;
|
private ArrayList<AudioMix> mMixes;
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new Builder with no audio mixes.
|
* Constructs a new Builder with no audio mixes.
|
||||||
|
* @param context the context for the policy
|
||||||
*/
|
*/
|
||||||
public Builder() {
|
public Builder(Context context) {
|
||||||
mMixes = new ArrayList<AudioMix>();
|
mMixes = new ArrayList<AudioMix>();
|
||||||
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,10 +113,115 @@ public class AudioPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public AudioPolicy build() {
|
public AudioPolicy build() {
|
||||||
return new AudioPolicy(new AudioPolicyConfig(mMixes));
|
return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public void setRegistration(String regId) {
|
||||||
|
mRegistrationId = regId;
|
||||||
|
mConfig.setRegistration(regId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean policyReadyToUse() {
|
||||||
|
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 checkMixReadyToUse(AudioMix mix, boolean forTrack)
|
||||||
|
throws IllegalArgumentException{
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
* 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.
|
||||||
|
* @param mix a non-null {@link AudioMix} instance whose routing flags was defined with
|
||||||
|
* {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy.
|
||||||
|
* @return a new {@link AudioRecord} instance whose data format is the one defined in the
|
||||||
|
* {@link AudioMix}, or null if this policy was not successfully registered
|
||||||
|
* with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
*/
|
||||||
|
public AudioRecord createAudioRecordSink(AudioMix mix) throws IllegalArgumentException {
|
||||||
|
if (!policyReadyToUse()) {
|
||||||
|
Log.e(TAG, "Cannot create AudioRecord sink for AudioMix");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
checkMixReadyToUse(mix, false/*not for an AudioTrack*/);
|
||||||
|
// 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(mix.getRegistration())
|
||||||
|
.build(),
|
||||||
|
mix.getFormat(),
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
* 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.
|
||||||
|
* @param mix a non-null {@link AudioMix} instance whose routing flags was defined with
|
||||||
|
* {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy.
|
||||||
|
* @returna new {@link AudioTrack} instance whose data format is the one defined in the
|
||||||
|
* {@link AudioMix}, or null if this policy was not successfully registered
|
||||||
|
* with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
*/
|
||||||
|
public AudioTrack createAudioTrackSource(AudioMix mix) throws IllegalArgumentException {
|
||||||
|
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(mix.getRegistration())
|
||||||
|
.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 int getStatus() {
|
public int getStatus() {
|
||||||
return mStatus;
|
return mStatus;
|
||||||
@@ -118,10 +237,9 @@ public class AudioPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@Override
|
public String toLogFriendlyString() {
|
||||||
public String toString () {
|
|
||||||
String textDump = new String("android.media.audiopolicy.AudioPolicy:\n");
|
String textDump = new String("android.media.audiopolicy.AudioPolicy:\n");
|
||||||
textDump += "config=" + mConfig.toString();
|
textDump += "config=" + mConfig.toLogFriendlyString();
|
||||||
return (textDump);
|
return (textDump);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,13 @@ public class AudioPolicyConfig implements Parcelable {
|
|||||||
|
|
||||||
private static final String TAG = "AudioPolicyConfig";
|
private static final String TAG = "AudioPolicyConfig";
|
||||||
|
|
||||||
ArrayList<AudioMix> mMixes;
|
protected ArrayList<AudioMix> mMixes;
|
||||||
|
|
||||||
|
protected String mRegistrationId = null;
|
||||||
|
|
||||||
|
protected AudioPolicyConfig(AudioPolicyConfig conf) {
|
||||||
|
mMixes = conf.mMixes;
|
||||||
|
}
|
||||||
|
|
||||||
AudioPolicyConfig(ArrayList<AudioMix> mixes) {
|
AudioPolicyConfig(ArrayList<AudioMix> mixes) {
|
||||||
mMixes = mixes;
|
mMixes = mixes;
|
||||||
@@ -117,7 +123,6 @@ public class AudioPolicyConfig implements Parcelable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
|
||||||
public static final Parcelable.Creator<AudioPolicyConfig> CREATOR
|
public static final Parcelable.Creator<AudioPolicyConfig> CREATOR
|
||||||
= new Parcelable.Creator<AudioPolicyConfig>() {
|
= new Parcelable.Creator<AudioPolicyConfig>() {
|
||||||
/**
|
/**
|
||||||
@@ -133,9 +138,7 @@ public class AudioPolicyConfig implements Parcelable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @hide */
|
public String toLogFriendlyString () {
|
||||||
@Override
|
|
||||||
public String toString () {
|
|
||||||
String textDump = new String("android.media.audiopolicy.AudioPolicyConfig:\n");
|
String textDump = new String("android.media.audiopolicy.AudioPolicyConfig:\n");
|
||||||
textDump += mMixes.size() + " AudioMix:\n";
|
textDump += mMixes.size() + " AudioMix:\n";
|
||||||
for(AudioMix mix : mMixes) {
|
for(AudioMix mix : mMixes) {
|
||||||
@@ -166,4 +169,13 @@ public class AudioPolicyConfig implements Parcelable {
|
|||||||
}
|
}
|
||||||
return textDump;
|
return textDump;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRegistration(String regId) {
|
||||||
|
mRegistrationId = regId;
|
||||||
|
int mixIndex = 0;
|
||||||
|
for (AudioMix mix : mMixes) {
|
||||||
|
mix.setRegistration(mRegistrationId + "mix:" + mixIndex++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user