Android外部オーディオポリシー登録プロセス


目次
 
背景
外部オーディオフォーカスポリシー
外部オーディオルーティングポリシー
背景
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");
                        }
                    }
    ******
}
                    

もちろん、デバイスが削除されると、このポリシーも削除する必要があり、読者は自分で実現することができます.