Android外部オーディオポリシー登録プロセス
目次
背景
外部オーディオフォーカスポリシー
外部オーディオルーティングポリシー
背景
Android P automovieバージョンでは、外部オーディオフォーカスポリシーと外部オーディオルーティングポリシーの2つの部分を含む外部オーディオポリシーの登録がサポートされています.
理由はgoogleの公式説明を直接見ることができて、大意は車載システムにとって、オーディオの焦点の需要はもっと複雑で、同時にオーディオのルートは携帯電話版に比べてもっと簡潔です(簡潔で簡単ではありませんて、車載のバージョンの上で、多くのオーディオのルートに対する策略は基本的にすべて使えません).
外部オーディオフォーカスポリシー
車載バージョンでは、オーディオに関連するCarAudioServiceが対応するCarモジュールで、setupDynamicRoutingが登録(CarAudioService.java):
AudioManagerを使用してシステムにポリシーを登録し、作成したAudioPolicyオブジェクトには、フォーカスおよびルーティングポリシーが含まれていることがわかります.プロセスは次のとおりです(CarAudioServices.java):
次にregisterAudioPolicyプロセスを参照し、AudioManagerによって実装されます(AudioManager.java).
AudioServiceインプリメンテーション(AudioService.java):
AudioPolicyProxyコンストラクション関数(AudioService.java)を次に示します.
まずaddFocusFollowerを参照してください.外部ポリシー(MediaFocusControl.java)にフォーカスリクエストを渡すことなく、フォーカスの変化のみを受動的に通知します.
次にsetFocusPolicyを参照すると、ローカルmFocusPolicyオブジェクト(MediaFocusControl.java)にフォーカスが割り当てられます.
申請フォーカスが適用されると、mFocusPolicyが空であるか否かが判断され、空でない場合、外部フォーカスポリシーにより論理判断(申請成功or失敗)が実現される(MediaFocusControl.java):
mFocusPolicyはAudioPolicyで実現する.JAvaでは、ここで拡張を補完します.外部フォーカスポリシーの判断が完了すると、結果はシステムに通知され、システムはアプリケーション(AudioManager.java)に返される.
このプロセスには200 msを超える時間制限があり、以下のように定義されています(AudioManager.java).
外部オーディオルーティングポリシー
接続MixesはAudioSystemを簡単に呼び出す.registerPolicyMixes(mMixes,true)はnative関数であり、実装中である.以下、registerPolicyMixes実装(android_media_AudioSystem.cpp)を参照する.
JavaオブジェクトをconvertAudioMixToNative関数でnativeオブジェクトに変換するには、AudioMix構造(AudioMix.java)を参照してください.
nativeはJavaに完全に対応するように構成されていますが、ここでは詳しく説明しません(AudioPolicy.h):
AudioSystem呼び出しAPS実装(AudioSystem.cpp):
最終的にAPM(AudioPolicyInterfaceImpl.cpp):
紙面は限られていて、AudioPolicyManager::registerPolicyMixesの実現は展開しないで、その本質は循環を通じて、それぞれLOOP_をBACK、RENDER対応のAudioMixは、以下のようにmPolicyMixesオブジェクトに登録されています.
出力については、定義したルーティングポリシーが次のように使用されます.
入力用:
注意ポリシーは必ずしもJavaで登録する必要はありません.例えば、USBスピーカを挿入する場合(現在はデバイスがサポートしなければならないことが前提です)、指定した音源タイプ(Mediaなど)はUSBスピーカで放送する必要がありますが、その他は影響を受けません.以下のコードを追加してダイナミックポリシーを追加することができます(上位粗初期化時にUSBデバイスがないため、直接上位方式で構成することはできません):
もちろん、デバイスが削除されると、このポリシーも削除する必要があり、読者は自分で実現することができます.
背景
外部オーディオフォーカスポリシー
外部オーディオルーティングポリシー
背景
Android P automovieバージョンでは、外部オーディオフォーカスポリシーと外部オーディオルーティングポリシーの2つの部分を含む外部オーディオポリシーの登録がサポートされています.
理由はgoogleの公式説明を直接見ることができて、大意は車載システムにとって、オーディオの焦点の需要はもっと複雑で、同時にオーディオのルートは携帯電話版に比べてもっと簡潔です(簡潔で簡単ではありませんて、車載のバージョンの上で、多くのオーディオのルートに対する策略は基本的にすべて使えません).
外部オーディオフォーカスポリシー
車載バージョンでは、オーディオに関連するCarAudioServiceが対応するCarモジュールで、setupDynamicRoutingが登録(CarAudioService.java):
AudioPolicy audioPolicy = getDynamicAudioPolicy();
int r = mAudioManager.registerAudioPolicy(audioPolicy);
if (r != AudioManager.SUCCESS) {
throw new RuntimeException("registerAudioPolicy failed " + r);
}
mAudioPolicy = audioPolicy;
AudioManagerを使用してシステムにポリシーを登録し、作成したAudioPolicyオブジェクトには、フォーカスおよびルーティングポリシーが含まれていることがわかります.プロセスは次のとおりです(CarAudioServices.java):
//1. policy build
AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);
//2.
//2.1 Mix build
AudioMixingRule.Builder mixingRuleBuilder = new AudioMixingRule.Builder();
//2.2 , , AudioMixingRule。 usage ,
// usage/streamType 。
mixingRuleBuilder.addRule(
new AudioAttributes.Builder().setUsage(usage).build(),
AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE);
//2.3 mix Mix
// info , 、 、
AudioFormat mixFormat = new AudioFormat.Builder()
.setSampleRate(info.getSampleRate())
.setEncoding(info.getEncodingFormat())
.setChannelMask(info.getChannelCount())
.build();
//deviceinfo ,RouteFlags ROUTE_FLAG_RENDER ,
AudioMix audioMix = new AudioMix.Builder(mixingRuleBuilder.build())
.setFormat(mixFormat)
.setDevice(info.getAudioDeviceInfo())
.setRouteFlags(AudioMix.ROUTE_FLAG_RENDER)
.build();
//2.4 Mix ,
builder.addMix(audioMix);
//3.
builder.setIsAudioFocusPolicy(true);
//mAudioPolicyFocusListener AudioPolicy.AudioPolicyFocusListener
builder.setAudioPolicyFocusListener(mAudioPolicyFocusListener);
//4. AudioPolicy
builder.build()
次にregisterAudioPolicyプロセスを参照し、AudioManagerによって実装されます(AudioManager.java).
//====================================================================
// Audio policy
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public int registerAudioPolicy(@NonNull AudioPolicy policy) {
......
//getConfig Mix
//cb
//hasFocusListener ,
//isVolumeController , 、 、 , 。
String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(),
policy.hasFocusListener(), policy.isFocusPolicy(), policy.isVolumeController());
if (regId == null) {
return ERROR;
} else {
// ,
policy.setRegistration(regId);
}
// successful registration
......
return SUCCESS;
}
AudioServiceインプリメンテーション(AudioService.java):
public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb,
boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) {
// , native Mix
AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);
......
// ,
AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener,isFocusPolicy, isVolumeController);
// binder
pcb.asBinder().linkToDeath(app, 0/*flags*/);
regId = app.getRegistrationId();
// 。
mAudioPolicies.put(pcb.asBinder(), app);
......
return regId;
}
AudioPolicyProxyコンストラクション関数(AudioService.java)を次に示します.
AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token,
boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) {
......
// mHasFocusListener
if (mHasFocusListener) {
mMediaFocusControl.addFocusFollower(mPolicyCallback);
// can only ever be true if there is a focus listener
// 。
if (isFocusPolicy) {
mIsFocusPolicy = true;
mMediaFocusControl.setFocusPolicy(mPolicyCallback);
}
}
//
if (mIsVolumeController) {
setExtVolumeController(mPolicyCallback);
}
// Mix
connectMixes();
}
まずaddFocusFollowerを参照してください.外部ポリシー(MediaFocusControl.java)にフォーカスリクエストを渡すことなく、フォーカスの変化のみを受動的に通知します.
private ArrayList mFocusFollowers = new ArrayList();
//addFocusFollower mFocusFollowers
void addFocusFollower(IAudioPolicyCallback ff) {
******
mFocusFollowers.add(ff);
notifyExtPolicyCurrentFocusAsync(ff);
******
}
//
void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) {
for (IAudioPolicyCallback pcb : mFocusFollowers) {
try {
// oneway
pcb.notifyAudioFocusGrant(afi, requestResult);
} catch (RemoteException e) {
Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
+ pcb.asBinder(), e);
}
}
}
//
void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) {
for (IAudioPolicyCallback pcb : mFocusFollowers) {
try {
// oneway
pcb.notifyAudioFocusLoss(afi, wasDispatched);
} catch (RemoteException e) {
Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback "
+ pcb.asBinder(), e);
}
}
}
次にsetFocusPolicyを参照すると、ローカルmFocusPolicyオブジェクト(MediaFocusControl.java)にフォーカスが割り当てられます.
void setFocusPolicy(IAudioPolicyCallback policy) {
if (policy == null) {
return;
}
synchronized (mAudioFocusLock) {
mFocusPolicy = policy;
}
}
申請フォーカスが適用されると、mFocusPolicyが空であるか否かが判断され、空でない場合、外部フォーカスポリシーにより論理判断(申請成功or失敗)が実現される(MediaFocusControl.java):
protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
int sdk, boolean forceDuck) {
******
synchronized(mAudioFocusLock) {
******
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, sdk);
} else {
afiForExtPolicy = null;
}
******
// external focus policy?
if (mFocusPolicy != null) {
// notifyExtFocusPolicyFocusRequest_syncAf
if (notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, fd, cb)) {
// stop handling focus request here as it is handled by external audio
// focus policy (return code will be handled in AudioManager)
return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY;
} else {
// an error occured, client already dead, bail early
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
}
}
}
boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi,
IAudioFocusDispatcher fd, IBinder cb) {
......
try {
//oneway
// notifyAudioFocusRequest
mFocusPolicy.notifyAudioFocusRequest(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
return true;
} catch (RemoteException e) {
Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback "
+ mFocusPolicy.asBinder(), e);
}
return false;
}
mFocusPolicyはAudioPolicyで実現する.JAvaでは、ここで拡張を補完します.外部フォーカスポリシーの判断が完了すると、結果はシステムに通知され、システムはアプリケーション(AudioManager.java)に返される.
/**
* @hide
* Set the result to the audio focus request received through
* {@link AudioPolicyFocusListener#onAudioFocusRequest(AudioFocusInfo, int)}.
* @param afi the information about the focus requester
* @param requestResult the result to the focus request to be passed to the requester
* @param ap a valid registered {@link AudioPolicy} configured as a focus policy.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setFocusRequestResult(@NonNull AudioFocusInfo afi,
@FocusRequestResult int requestResult, @NonNull AudioPolicy ap) {
if (afi == null) {
throw new IllegalArgumentException("Illegal null AudioFocusInfo");
}
if (ap == null) {
throw new IllegalArgumentException("Illegal null AudioPolicy");
}
final IAudioService service = getService();
try {
service.setFocusRequestResultFromExtPolicy(afi, requestResult, ap.cb());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
このプロセスには200 msを超える時間制限があり、以下のように定義されています(AudioManager.java).
/**
* Timeout duration in ms when waiting on an external focus policy for the result for a
* focus request
*/
private static final int EXT_FOCUS_POLICY_TIMEOUT_MS = 200
外部オーディオルーティングポリシー
接続MixesはAudioSystemを簡単に呼び出す.registerPolicyMixes(mMixes,true)はnative関数であり、実装中である.以下、registerPolicyMixes実装(android_media_AudioSystem.cpp)を参照する.
static jint
android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz,
jobject jMixesList, jboolean registration)
{
......
status_t status;
jint jStatus;
jobject jAudioMix = NULL;
Vector mixes;
// Java AudioMix native
for (jint i = 0; i < numMixes; i++) {
jAudioMix = env->GetObjectArrayElement(jMixes, i);
if (!env->IsInstanceOf(jAudioMix, gAudioMixClass)) {
jStatus = (jint)AUDIO_JAVA_BAD_VALUE;
goto exit;
}
AudioMix mix;
jStatus = convertAudioMixToNative(env, &mix, jAudioMix);
env->DeleteLocalRef(jAudioMix);
jAudioMix = NULL;
if (jStatus != AUDIO_JAVA_SUCCESS) {
goto exit;
}
mixes.add(mix);
}
......
// AudioSystem::registerPolicyMixes , AudioSystem native Java
status = AudioSystem::registerPolicyMixes(mixes, registration);
......
return jStatus;
}
JavaオブジェクトをconvertAudioMixToNative関数でnativeオブジェクトに変換するには、AudioMix構造(AudioMix.java)を参照してください.
private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags, int callbackFlags,
int deviceType, String deviceAddress) {
// Rule
mRule = rule;
// 、 、
mFormat = format;
/*
* ROUTE_FLAG_RENDER ROUTE_FLAG_LOOP_BACK , mMixType
*/
mRouteFlags = routeFlags;
/*
* AudioMix.MIX_TYPE_PLAYERS AudioMix.MIX_TYPE_RECORDERS
* Rule(AudioMixingRule#RULE_MATCH) :
* players => RULE_MATCH_ATTRIBUTE_USAGE、RULE_MATCH_UID
* recorders => RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET
*/
mMixType = rule.getTargetMixType();
// ,
mCallbackFlags = callbackFlags;
// AudioSystem.DEVICE_*
mDeviceSystemType = deviceType;
//
mDeviceAddress = (deviceAddress == null) ? new String("") : deviceAddress;
}
nativeはJavaに完全に対応するように構成されていますが、ここでは詳しく説明しません(AudioPolicy.h):
class AudioMix {
public:
// flag on an AudioMix indicating the activity on this mix (IDLE, MIXING)
// must be reported through the AudioPolicyClient interface
static const uint32_t kCbFlagNotifyActivity = 0x1;
AudioMix() {}
AudioMix(Vector criteria, uint32_t mixType, audio_config_t format,
uint32_t routeFlags, String8 registrationId, uint32_t flags) :
mCriteria(criteria), mMixType(mixType), mFormat(format),
mRouteFlags(routeFlags), mDeviceAddress(registrationId), mCbFlags(flags){}
status_t readFromParcel(Parcel *parcel);
status_t writeToParcel(Parcel *parcel) const;
Vector mCriteria;
uint32_t mMixType;
audio_config_t mFormat;
uint32_t mRouteFlags;
audio_devices_t mDeviceType;
String8 mDeviceAddress;
uint32_t mCbFlags; // flags indicating which callbacks to use, see kCbFlag*
};
AudioSystem呼び出しAPS実装(AudioSystem.cpp):
status_t AudioSystem::registerPolicyMixes(const Vector& mixes, bool registration)
{
const sp& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->registerPolicyMixes(mixes, registration);
}
最終的にAPM(AudioPolicyInterfaceImpl.cpp):
status_t AudioPolicyService::registerPolicyMixes(const Vector& mixes, bool registration)
{
......
AutoCallerClear acc;
// true,
if (registration) {
return mAudioPolicyManager->registerPolicyMixes(mixes);
} else {
return mAudioPolicyManager->unregisterPolicyMixes(mixes);
}
}
紙面は限られていて、AudioPolicyManager::registerPolicyMixesの実現は展開しないで、その本質は循環を通じて、それぞれLOOP_をBACK、RENDER対応のAudioMixは、以下のようにmPolicyMixesオブジェクトに登録されています.
// address , AudioMix deviceAddress , AudioPatch ,
mPolicyMixes.registerMix(address, mixes[i], desc)
出力については、定義したルーティングポリシーが次のように使用されます.
// , , 。
status_t AudioPolicyManager::checkOutputsForDevice(const sp& devDesc,
audio_policy_dev_state_t state,
SortedVector& outputs,
const String8& address)
{
******
addOutput(output, desc);
// , , ,
if (device_distinguishes_on_address(device) && address != "0") {
sp policyMix;
if (mPolicyMixes.getAudioPolicyMix(address, policyMix) != NO_ERROR) {
ALOGE("checkOutputsForDevice() cannot find policy for address %s",
address.string());
}
policyMix->setOutput(desc);
desc->mPolicyMix = policyMix;
}
******
}
// ,
status_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *output,
audio_session_t session,
audio_stream_type_t *stream,
uid_t uid,
const audio_config_t *config,
audio_output_flags_t *flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId)
{
......
sp desc;
// mPolicyMixes arrt
if (mPolicyMixes.getOutputForAttr(attributes, uid, desc) == NO_ERROR) {
ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
if (!audio_has_proportional_frames(config->format)) {
return BAD_VALUE;
}
*stream = streamTypefromAttributesInt(&attributes);
*output = desc->mIoHandle;
ALOGV("getOutputForAttr() returns output %d", *output);
return NO_ERROR;
}
......
}
入力用:
// ,
status_t AudioPolicyManager::getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
uid_t uid,
const audio_config_base_t *config,
audio_input_flags_t flags,
audio_port_handle_t *selectedDeviceId,
input_type_t *inputType,
audio_port_handle_t *portId)
{
......
audio_devices_t device;
//
if (inputSource == AUDIO_SOURCE_REMOTE_SUBMIX &&
strncmp(attr->tags, "addr=", strlen("addr=")) == 0) {
status = mPolicyMixes.getInputMixForAttr(*attr, &policyMix);
if (status != NO_ERROR) {
goto error;
}
*inputType = API_INPUT_MIX_EXT_POLICY_REROUTE;
device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
address = String8(attr->tags + strlen("addr="));
} else {
// , ,
device = getDeviceAndMixForInputSource(inputSource, &policyMix);
}
......
}
audio_devices_t AudioPolicyManager::getDeviceAndMixForInputSource(audio_source_t inputSource,
sp *policyMix)
{
audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
audio_devices_t selectedDeviceFromMix =
mPolicyMixes.getDeviceAndMixForInputSource(inputSource, availableDeviceTypes, policyMix);
// ,
if (selectedDeviceFromMix != AUDIO_DEVICE_NONE) {
return selectedDeviceFromMix;
}
return getDeviceForInputSource(inputSource);
}
注意ポリシーは必ずしもJavaで登録する必要はありません.例えば、USBスピーカを挿入する場合(現在はデバイスがサポートしなければならないことが前提です)、指定した音源タイプ(Mediaなど)はUSBスピーカで放送する必要がありますが、その他は影響を受けません.以下のコードを追加してダイナミックポリシーを追加することができます(上位粗初期化時にUSBデバイスがないため、直接上位方式で構成することはできません):
status_t AudioPolicyManager::checkOutputsForDevice(const sp& devDesc,
audio_policy_dev_state_t state,
SortedVector& outputs,
const String8& address)
{
******
// USB
} else if(FORCE_USB_SPEAKER && (device & AUDIO_DEVICE_OUT_USB_HEADSET)){
AudioMix audioMix;
audioMix.mMixType = MIX_TYPE_PLAYERS;//AudioMix.MIX_TYPE_PLAYERS
audioMix.mRouteFlags = MIX_ROUTE_FLAG_RENDER;//AudioMix.ROUTE_FLAG_RENDER;
audioMix.mDeviceType = desc->device();
audioMix.mDeviceAddress = address;
audioMix.mCbFlags = 0;//AudioMix.mCallbackFlags
audioMix.mFormat.sample_rate = desc->mSamplingRate;
audioMix.mFormat.channel_mask = desc->mChannelMask;
audioMix.mFormat.format = desc->mFormat;
AudioMixMatchCriterion criterion1;
criterion1.mRule = RULE_MATCH_ATTRIBUTE_USAGE;
criterion1.mValue.mUsage = AUDIO_USAGE_MEDIA;
AudioMixMatchCriterion criterion2;
criterion2.mRule = RULE_MATCH_ATTRIBUTE_USAGE;
criterion2.mValue.mUsage = AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
audioMix.mCriteria.add(criterion1);
audioMix.mCriteria.add(criterion2);
if (mPolicyMixes.registerMix(address, audioMix, desc) == NO_ERROR) {
ALOGD("Success to registerMix for tbox output device");
}else{
ALOGD("Failed to registerMix for tbox output device");
}
}
******
}
もちろん、デバイスが削除されると、このポリシーも削除する必要があり、読者は自分で実現することができます.