Android 9.0 AAudio源码分析

Posted ……蓦然回首

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 9.0 AAudio源码分析相关的知识,希望对你有一定的参考价值。

android 9.0 AAudio源码分析(一)


提示:再读文章之前可以先学习一下Binder和MMAP的知识


前言

因为网上目前还没有关于安卓AAudio方面的原理分析,所以笔者通过研究安卓9.0源码,总结了一些偏向于底层的一些东西,希望可以帮助到大家


一、AAudio是什么

AAudio 是在 Android O 版本中引入的全新 Android C API。此 API 专为需要低延迟的高性能音频应用而设计。应用通过读取数据并将数据写入流来与 AAudio 进行通信

二、AAudio源码解析

1.启动

(1)系统服务启动main_audioserver.cpp

xref: /frameworks/av/media/audioserver/main_audioserver.cpp

// AAudioService should only be used in OC-MR1 and later.
// And only enable the AAudioService if the system MMAP policy explicitly allows it.
// This prevents a client from misusing AAudioService when it is not supported.
//AAudioService只能在OC-MR1及更高版本中使用
//并且只有在系统MMAP策略明确允许的情况下才启用AAudioService
//这可以防止客户端在不支持AAudioService时误用它

aaudio_policy_t mmapPolicy = property_get_int32(AAUDIO_PROP_MMAP_POLICY,
                                                AAUDIO_POLICY_NEVER);
if (mmapPolicy == AAUDIO_POLICY_AUTO || mmapPolicy == AAUDIO_POLICY_ALWAYS) {
    AAudioService::instantiate();
}

可以看到是有一个AAudioService来初始化的。

(2)AAudioService是什么?

先来看它都继承了什么:
binder不用多说,BnAAudioService和AAudioServiceInterface要看一下

xref: /frameworks/av/services/oboeservice/AAudioService.h

class AAudioService :
    public BinderService<AAudioService>,
    public BnAAudioService,
    public aaudio::AAudioServiceInterface
{
    friend class BinderService<AAudioService>;

先来看BnAAudioService:

从名字来看就是一个服务
这个类写在了IAAudioService.h的头文件中
而client的各种使用AAudio的操作,都通过层层调用及参数包装汇聚到onTransact(),再由onTransact()分发到真正的目标方法执行

xref: /frameworks/av/media/libaaudio/src/binding/IAAudioService.h

class BnAAudioService : public BnInterface<IAAudioService> {
public:
    virtual status_t onTransact(uint32_t code, const Parcel& data,
                                Parcel* reply, uint32_t flags = 0);

};

具体都有哪些方法呢?

class IAAudioService : public IInterface {
public:

    DECLARE_META_INTERFACE(AAudioService);

    // Register an object to receive audio input/output change and track notifications.
    // For a given calling pid, AAudio service disregards any registrations after the first.
    // Thus the IAAudioClient must be a singleton per process.
    virtual void registerClient(const sp<IAAudioClient>& client) = 0;

    /**
     * @param request info needed to create the stream
     * @param configuration contains information about the created stream
     * @return handle to the stream or a negative error
     */
    virtual aaudio::aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
                                     aaudio::AAudioStreamConfiguration &configurationOutput) = 0;

    virtual aaudio_result_t closeStream(aaudio::aaudio_handle_t streamHandle) = 0;

    /* Get an immutable description of the in-memory queues
    * used to communicate with the underlying HAL or Service.
    */
    virtual aaudio_result_t getStreamDescription(aaudio::aaudio_handle_t streamHandle,
                                               aaudio::AudioEndpointParcelable &parcelable) = 0;

    /**
     * Start the flow of data.
     * This is asynchronous. When complete, the service will send a STARTED event.
     */
    virtual aaudio_result_t startStream(aaudio::aaudio_handle_t streamHandle) = 0;

    /**
     * Stop the flow of data such that start() can resume without loss of data.
     * This is asynchronous. When complete, the service will send a PAUSED event.
     */
    virtual aaudio_result_t pauseStream(aaudio::aaudio_handle_t streamHandle) = 0;

    /**
     * Stop the flow of data such that the data currently in the buffer is played.
     * This is asynchronous. When complete, the service will send a STOPPED event.
     */
    virtual aaudio_result_t stopStream(aaudio::aaudio_handle_t streamHandle) = 0;

    /**
     *  Discard any data held by the underlying HAL or Service.
     * This is asynchronous. When complete, the service will send a FLUSHED event.
     */
    virtual aaudio_result_t flushStream(aaudio::aaudio_handle_t streamHandle) = 0;

    /**
     * Manage the specified thread as a low latency audio thread.
     */
    virtual aaudio_result_t registerAudioThread(aaudio::aaudio_handle_t streamHandle,
                                              pid_t clientThreadId,
                                              int64_t periodNanoseconds) = 0;

    virtual aaudio_result_t unregisterAudioThread(aaudio::aaudio_handle_t streamHandle,
                                                pid_t clientThreadId) = 0;
};

这些函数能够让用户很方便的使用AAudio实现播放或者录制等等

顺便看一下IAAudioService.cpp文件都干了啥?
xref: /frameworks/av/media/libaaudio/src/binding/IAAudioService.cpp

/**
 * This is used by the AAudio Client to talk to the AAudio Service.
 *
 * The order of parameters in the Parcels must match with code in AAudioService.cpp.
 */
class BpAAudioService : public BpInterface<IAAudioService>
{
public:
    explicit BpAAudioService(const sp<IBinder>& impl) 
        : BpInterface<IAAudioService>(impl)
    {
    }

    void registerClient(const sp<IAAudioClient>& client) override
    {
        Parcel data, reply;
        data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
        data.writeStrongBinder(IInterface::asBinder(client));
        remote()->transact(REGISTER_CLIENT, data, &reply);
    }

    aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
                               aaudio::AAudioStreamConfiguration &configurationOutput) override {
        Parcel data, reply;
        // send command
        data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
        // request.dump();
        request.writeToParcel(&data);
        status_t err = remote()->transact(OPEN_STREAM, data, &reply);
        if (err != NO_ERROR) {
            ALOGE("BpAAudioService::client openStream transact failed %d", err);
            return AAudioConvert_androidToAAudioResult(err);
        }
        // parse reply
        aaudio_handle_t stream;
        err = reply.readInt32(&stream);
        if (err != NO_ERROR) {
            ALOGE("BpAAudioService::client transact(OPEN_STREAM) readInt %d", err);
            return AAudioConvert_androidToAAudioResult(err);
        } else if (stream < 0) {
            ALOGE("BpAAudioService::client OPEN_STREAM passed stream %d", stream);
            return stream;
        }
        err = configurationOutput.readFromParcel(&reply);
        if (err != NO_ERROR) {
            ALOGE("BpAAudioService::client openStream readFromParcel failed %d", err);
            closeStream(stream);
            return AAudioConvert_androidToAAudioResult(err);
        }
        return stream;
    }

...

};

// Implement an interface to the service.
// This is here so that you don't have to link with libaaudio static library.
IMPLEMENT_META_INTERFACE(AAudioService, "IAAudioService");

// The order of parameters in the Parcels must match with code in BpAAudioService

status_t BnAAudioService::onTransact(uint32_t code, const Parcel& data,
                                        Parcel* reply, uint32_t flags) {
    aaudio_handle_t streamHandle;
    aaudio::AAudioStreamRequest request;
    aaudio::AAudioStreamConfiguration configuration;
    pid_t tid;
    int64_t nanoseconds;
    aaudio_result_t result;
    status_t status = NO_ERROR;
    ALOGV("BnAAudioService::onTransact(%i) %i", code, flags);

    switch(code) {
        case REGISTER_CLIENT: {
            CHECK_INTERFACE(IAAudioService, data, reply);
            sp<IAAudioClient> client = interface_cast<IAAudioClient>(
                    data.readStrongBinder());
            registerClient(client);
            return NO_ERROR;
        } break;

        case OPEN_STREAM: {
            CHECK_INTERFACE(IAAudioService, data, reply);
            request.readFromParcel(&data);
            result = request.validate();
            if (result != AAUDIO_OK) {
                streamHandle = result;
            } else {
                //ALOGD("BnAAudioService::client openStream request dump --------------------");
                //request.dump();
                // Override the uid and pid from the client in case they are incorrect.
                request.setUserId(IPCThreadState::self()->getCallingUid());
                request.setProcessId(IPCThreadState::self()->getCallingPid());
                streamHandle = openStream(request, configuration);
                //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X",
                //        streamHandle);
            }
            reply->writeInt32(streamHandle);
            configuration.writeToParcel(reply);
            return NO_ERROR;
        } break;

...

        default:
            // ALOGW("BnAAudioService::onTransact not handled %u", code);
            return BBinder::onTransact(code, data, reply, flags);
    }
}

} /* namespace android */

其中声明了BpAAudioService类,实现了这些函数
最终还是经过transact调到了BnAAudioService,然后根据参数分发处理
整个过程用到了跨进程通信的方法:Binder

再来看AAudioService的另一个继承:AAudioServiceInterface

xref: /frameworks/av/media/libaaudio/src/binding/AAudioServiceInterface.h

/**
 * This has the same methods as IAAudioService but without the Binder features.
 *
 * It allows us to abstract the Binder interface and use an AudioStreamInternal
 * both in the client and in the service.
 */

只看注释,因为定义的方法和IAAudioService里面几乎一样,就是少了Binder
所以也就可以单纯的把它看作一个接口,客户端和服务端都有继承AAudioServiceInterface,统一了客户端和服务端的接口

(3)其他几个重要的类的介绍

IAAudioClient:
xref: /frameworks/av/media/libaaudio/src/binding/AAudioBinderClient.h

通过绑定器与服务对话来实现AAudioServiceInterface里面的方法,就是我们上面说到的客户端
在其内部通过从SM(ServiceManager)里面拿到AAudioService的引用,绑定之后使用

class AAudioBinderClient : public virtual android::RefBase
        , public AAudioServiceInterface
        , public android::Singleton<AAudioBinderClient> {

加上它的继承的一些类来看,会更加一目了然。

AAudioBinderClient
xref: /frameworks/av/media/libaaudio/src/binding/IAAudioClient.h

这个类是用来service与client通信的,其中主要内容就是流 状态的变化

// Interface (our AIDL) - client methods called by service
class IAAudioClient : public IInterface {
public:

    DECLARE_META_INTERFACE(AAudioClient);

    virtual void onStreamChange(aaudio::aaudio_handle_t handle, int32_t opcode, int32_t value) = 0;

};

class BnAAudioClient : public BnInterface<IAAudioClient> {
public:
    virtual status_t onTransact(uint32_t code, const Parcel& data,
                                Parcel* reply, uint32_t flags = 0);
};

} /* namespace android */

总结:
关于AAudioSevice,里面用了很多Binder的知识,把Binder看透了也就了解了这整个调用的流程

Binder流程如图:
Binder流程

2.工作原理

AAudioService提供了很多供我们使用它的方法,接下来就是对其中几个重要的方法进行剖析,了解其工作原理

(1)openStream:

先来看一张时序图:

在这里插入图片描述

打开流的过程如图所示,其中主要会分两种情况:
共享模式:多个音频流可以同时访问该音频设备,该模式下音频的延迟略高于独占模式
独占模式:只有该音频流能访问该音频设备,其它音频流拒绝访问,该模式下 音频流 性能高 , 延迟低
注意:如果不再使用该音频设备,需要马上释放音频流,以免影响其它音频流访问该音频设备(独占模式)

接下来来看代码:
xref: /frameworks/av/services/oboeservice/AAudioService.cpp

aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
                                          aaudio::AAudioStreamConfiguration &configurationOutput) {
    aaudio_result_t result = AAUDIO_OK;
    sp<AAudioServiceStreamBase> serviceStream;
    const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
    bool sharingModeMatchRequired = request.isSharingModeMatchRequired();
    aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();

    // Enforce limit on client processes.
    //对客户端进程实施限制
    //这里面规定每个进程都有最大音频流数
    //当超过这个最大音频流数量时,将不能继续执行函数
    pid_t pid = request.getProcessId();
    if (pid != mAudioClient.clientPid) {
        int32_t count = AAudioClientTracker::getInstance().getStreamCount(pid);
        if (count >= MAX_STREAMS_PER_PROCESS) {
            ALOGE("openStream(): exceeded max streams per process %d >= %d",
                  count,  MAX_STREAMS_PER_PROCESS);
            return AAUDIO_ERROR_UNAVAILABLE;
        }
    }
    
    //传输模式检测,只允许共享模式或者独占模式
    if (sharingMode != AAUDIO_SHARING_MODE_EXCLUSIVE && sharingMode != AAUDIO_SHARING_MODE_SHARED) {
        ALOGE("openStream(): unrecognized sharing mode = %d", sharingMode);
        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
    }

    //独占模式的情况
    if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
        // only trust audioserver for in service indication
        //这个inService变量代表着这个流是不是被AAudioService单独打开的(没有客户端)
        bool inService = false;
        if (mAudioClient.clientPid == IPCThreadState::self()->getCallingPid() &&
                mAudioClient.clientUid == IPCThreadState::self()->getCallingUid()) {
            inService = request.isInService();
        }
        //之后就可以new一个独占模式专用的对象来打开流
        //传递了两个参数:一个是AAudioService的指针,一个就是inService
        //在之后对流进行其他操作时会用到inService这个参数
        serviceStream = new AAudioServiceStreamMMAP(*this, inService);
        result = serviceStream->open(request);
        if (result != AAUDIO_OK) {
            // Clear it so we can possibly fall back to using a shared stream.
            ALOGW("openStream(), could not open in EXCLUSIVE mode");
            serviceStream.clear();
        }
    }

    // if SHARED requested or if EXCLUSIVE failed
    //接下来是共享模式打开流
    //当创建独占模式的流失败并且传输模式与请求的模式不同时,会以共享模式打开流
    if (sharingMode == AAUDIO_SHARING_MODE_SHARED
         || (serviceStream.get() == nullptr && !sharingModeMatchRequired)) {
        serviceStream =  new AAudioServiceStreamShared(*this);
        result = serviceStream->open(request);
    }
    
    //创建不成功
    if (result != AAUDIO_OK) {
        serviceStream.clear();
        ALOGE("openStream(): failed, return %d = %s",
              result, AAudio_convertResultToText(result));
        return result;
    } else {
        //创建成功
        //生成这个AAduio流服务的句柄返回供客户端调用
        aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get());
        ALOGD("openStream(): handle = 0x%08X", handle);
        serviceStream->setHandle(handle);
        pid_t pid = request.getProcessId();
        //将这个创建好的音频流注册为客户端用来发送数据
        AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
        configurationOutput.copyFrom(*serviceStream);
        return handle;
    }
}

接下来分析独占模式下是什么情况
xref: /frameworks/av/services/oboeservice/AAudioServiceStreamMMAP.cpp


//这些对应于独占模式MMAP客户端流。
//它独占使用一个AAudioServiceEndpointMMAP与底层设备或端口通信。
//这个服务流的核心就是使用MMAP缓冲区

//继承了AAudioServiceStreamBase
class AAudioServiceStreamMMAP : public AAudioServiceStreamBase

// Open stream on HAL and pass information about the shared memory buffer back to the client.
//打开HAL上的流并将有关共享内存缓冲区的信息传递回客户端。
aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request) {

    sp<AAudioServiceStreamMMAP> keep(this);
    
    //带着独占模式的参数,调用AAudioServiceStreamBase的open函数
    aaudio_result_t result = AAudioServiceStreamBase::open(request,
                                                           AAUDIO_SHARING_MODE_EXCLUSIVE);
    if (result != AAUDIO_OK) {
        return result;
    }

    //把这个mServiceEndpointWeak升级为强指针,看它是不是还存活
    //mServiceEndpoint变量可以由多个线程访问
    //因此,我们通过本地向智能指针升级弱指针来访问它,这是线程安全的
    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
    if (endpoint == nullptr) {
        ALOGE("%s() has no endpoint", __func__);
        return AAUDIO_ERROR_INVALID_STATE;
    }
    
    //注册这个AAudioServiceStreamMMAP流
    //AAudioServiceEndpoint(endpoint)由AAudioServiceStreamBase的子类使用,与底层音频设备或端口通信
    //保证流的操作是线程安全的
    result = endpoint->registerStream(keep);
    if (result != AAUDIO_OK) {
        return result;
    }

    setState(AAUDIO_STREAM_STATE_OPEN);

    return AAUDIO_OK;
}

继续分析AAudioServiceStreamBase::open
xref: /frameworks/av/services/oboeservice/AAudioServiceStreamBase.cpp

//AAudioServiceStreamBase的每个实例都对应于一个客户机流。
//它使用AAudioServiceEndpoint的子类与底层设备或端口通信。

aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
                                              aaudio_sharing_mode_t sharingMode) {
    AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
    aaudio_result_t result = AAUDIO_OK;

    mMmapClient.clientUid = request.getUserId();
    mMmapClient.clientPid = request.getProcessId();
    mMmapClient.packageName.setTo(String16("")); // TODO What should we do here?

    // Limit scope of lock to avoid recursive lock in close().
    //限制锁的作用域以避免close()中的递归锁。
    //判断mUpMessageQueueLock是否为空,也就是说看它是否是第一次打开流
    {
        std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
        if (mUpMessageQueue != nullptr) {
            ALOGE("%s() called twice", __func__);
            return AAUDIO_ERROR_INVALID_STATE;
        }

        //创建共享内存
        mUpMessageQueue = new SharedRingBuffer();
        result = mUpMessageQueue->allocate(sizeof(AAudioServiceMessage),
                                           QUEUE_UP_CAPACITY_COMMANDS);
        if (result != AAUDIO_OK) {
            goto error;
        }

        // This is not protected by a lock because the stream cannot be
        // referenced until the service returns a handle to the client.
        // So only one thread can open a stream.
        //这不受锁的保护,因为在服务向客户端返回句柄之前,流不能被引用。
        //所以只有一个线程可以打开一个流。
        mServiceEndpoint = mEndpointManager.openEndpoint(mAudioServiceAndroid 9.0 AAudio源码分析

Android 9.0 AAudio源码分析

Android 9.0 AAudio源码分析

错误记录Oboe / AAudio 播放器报错 ( onEventFromServer - AAUDIO_SERVICE_EVENT_DISCONNECTED - FIFO cleared )(代码片

Android Init进程分析番外篇:9.0的init进程

Android 9.0 SQLiteCantOpenDatabaseException SQLITE_CANTOPEN(不支持WAL模式)源码分析定位