AudioServer初始化过程

AudioServer是被init拉起来的一个deamon程序,其rc文件如下:

bash 复制代码
//frameworks/av/media/audioserver/audioserver.rc
service audioserver /system/bin/audioserver
    class core
    user audioserver
    # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
    group audio camera drmrpc media mediadrm net_bt net_bt_admin net_bw_acct wakelock
    capabilities BLOCK_SUSPEND
    # match rtprio cur / max with sensor service as we handle AR/VR HID sensor data.
    rlimit rtprio 10 10
    ioprio rt 4
    task_profiles ProcessCapacityHigh HighPerformance
    onrestart restart vendor.audio-hal
    onrestart restart vendor.audio-hal-aidl
    onrestart restart vendor.audio-effect-hal-aidl
    onrestart restart vendor.audio-hal-4-0-msd
    onrestart restart audio_proxy_service

audioserver 是 Android 系统中一个至关重要的原生守护进程(native daemon)。它的核心任务是初始化并托管 Android 系统中最关键的几个音频服务,包括:

  • AudioFlinger: 负责所有音频数据的混音、处理和最终输出到硬件。它是音频系统的核心。
  • AudioPolicyService: 负责音频策略管理,如决定音频路由(声音从扬声器、耳机还是蓝牙播放)、音量控制、设备选择等。
  • AAudioService: 提供现代化的低延迟音频 API(AAudio)的后端服务。

main函数的职责就是按照正确的顺序创建、初始化这些服务,并将它们注册到系统的 ServiceManager 中,以便其他应用和系统服务可以访问它们。

cpp 复制代码
// frameworks/av/media/audioserver/main_audioserver.cpp
int main(int argc __unused, char **argv __unused)
{
    SLOGI("%s: starting", __func__);
    // 获取当前系统时间并保存,用于在函数末尾计算整个初始化过程耗费了多长时间
    const auto startTime = std::chrono::steady_clock::now();
    // 限制当前进程(audioserver)的最大内存使用量,使用 setrlimit(RLIMIT_AS) 限制进程可以
    // 分配的内存量。要使用的值将从指定的系统属性中读取;如果该属性不存在,
    // 则将使用指定的字节数或总内存的指定百分比,以较小者为准。
    // 参数1 "audio.maxmem" : 对应的ro.audio.maxmem系统属性名
    // 参数2 : 绝对上限值(512MB)
    // 参数3 : 相对物理内存的百分比上限(20%)
    // 这可以防止音频服务出现内存泄漏时耗尽整个系统的内存
    limitProcessMemory(
        "audio.maxmem", /* "ro.audio.maxmem", property that defines limit */
        (size_t)512 * (1 << 20), /* SIZE_MAX, upper limit in bytes */
        20 /* upper limit as percentage of physical RAM */);

    // 忽略 SIGPIPE 信号。在进行 Socket 或管道通信时,如果对端已经关闭,
    // 本端继续写入会导致程序收到 SIGPIPE 信号并默认崩溃。忽略此信号可让程序改为接收一个
    // 错误码而不是直接崩溃
    signal(SIGPIPE, SIG_IGN);

    // 配置 HIDL/AIDL 的硬件抽象层(HAL)RPC 通信线程池,最大线程数为 4。
    // false 表示当前调用者稍后不会主动加入该线程池
    android::hardware::configureRpcThreadpool(4, false /*callerWillJoin*/);

    // 启动 Binder 线程池。这是为了让 audioserver 
    // 能够处理来自其他进程的 Binder 回调和跨进程调用
    ProcessState::self()->startThreadPool();

    // 为了避免 AudioFlinger 提前暴露给外部导致在 AudioPolicy 初始化完成前
    // 被错误调用从而引发崩溃或超时,代码选择在本地同一个进程内先创建好这两个服务,
    // 相互绑定后,再统一注册到 ServiceManager。
    const auto af = sp<AudioFlinger>::make();
    const auto afAdapter = sp<AudioFlingerServerAdapter>::make(af);
    // 其af设置为当前进程的本地实例-保存到AudioSystem
    AudioSystem::setLocalAudioFlinger(af);

    const auto aps = sp<AudioPolicyService>::make();
    // 将刚刚创建的音频策略服务实例传递给 AudioFlinger,完成它们在内部的相互绑定
    af->initAudioPolicyLocal(aps);
    // 其aps设置为当前进程的本地实例
    AudioSystem::setLocalAudioPolicyService(aps);

    // Start initialization of internally managed audio objects such as Device Effects.
    // 通知 AudioPolicyService 整个音频系统内部对象已经准备就绪,可以开始初始化底层音频设备
    //(如加载 Device Effects 等)
    aps->onAudioSystemReady();

    // Add AudioFlinger and AudioPolicy to ServiceManager.
    sp<IServiceManager> sm = defaultServiceManager();
    // 将afAdapter注册为media.audio_flinger服务
    sm->addService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME), afAdapter,
            false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
    // 将aps注册为media.audio_policy服务
    sm->addService(String16(AudioPolicyService::getServiceName()), aps,
            false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);

    // 准备查询系统是否支持 MMAP(内存映射)模式的音频传输。MMAP 是实现 
    // AAudio(Android 低延迟音频 API)的关键机制
    std::vector<AudioMMapPolicyInfo> policyInfos;
    // 向底层查询当前的 MMAP 策略信息并存入 policyInfos 列表中。
    status_t status = AudioSystem::getMmapPolicyInfos(
            AudioMMapPolicyType::DEFAULT, &policyInfos);

    // policyInfos 中任意一个(std::any_of)策略是 AUTO 或 ALWAYS
    //(即系统支持并允许 MMAP 机制)
    if (status == NO_ERROR &&
        std::any_of(policyInfos.begin(), policyInfos.end(), [](const auto& info) {
                return info.mmapPolicy == AudioMMapPolicy::AUTO ||
                       info.mmapPolicy == AudioMMapPolicy::ALWAYS;
        })) {
        // 实例化并启动 AAudioService 服务
        AAudioService::instantiate();
    } else {
        // ......
    }
    // 获取初始化完成时的当前时间
    const auto endTime = std::chrono::steady_clock::now();
    // 通知 AudioFlinger 初始化流程彻底完成
    af->startupFinished();
    using FloatMillis = std::chrono::duration<float, std::milli>;
    const float timeTaken = std::chrono::duration_cast<FloatMillis>(
            endTime - startTime).count();
    // 显示整个音频服务启动初始化所消耗的时间
    SLOGI("%s: initialization done in %.3f ms, joining thread pool", __func__, timeTaken);
    
    // 调用这一行后,主线程将无限期地阻塞在这里,专门负责监听和处理来自其他进程的 
    // Binder 请求(也就是客户端发来的音频请求)。直到系统关机或 audioserver 进程被杀死,
    // 程序才可能退出
    IPCThreadState::self()->joinThreadPool();
}

limitProcessMemory

限制当前进程(audioserver)的最大内存使用量,以防止内存泄漏导致耗尽整个系统的内存。使用 setrlimit(RLIMIT_AS) 限制进程可以分配的内存量。要使用的值将从指定的系统属性中读取;如果该属性不存在,则将使用指定的字节数或总内存的指定百分比,以较小者为准。

  • 如果系统使用了 Scudo 内存分配器(Android 11 及以后的默认分配器),代码调用了 __scudo_set_rss_limit,它严格限制的是进程的 RSS(常驻内存集,即实际占用的物理内存)
  • 如果走到了底部的 android_mallopt(M_SET_ALLOCATION_LIMIT_BYTES, ...),它限制的是 malloc 堆分配的总字节数上限。虽然 malloc 申请的是虚拟内存,但这种限制间接也限制了物理内存的增长。

参数说明 :

  • property:系统属性名称(例如上文传进来的 "audio.maxmem"),允许通过修改系统属性来强制覆盖内存限制。
  • numberOfBytes:硬性限制的字节数上限(例如传入的 512MB)。
  • percentageOfTotalMem:相对于设备总物理内存的百分比上限(例如传入的 20%)。
cpp 复制代码
// frameworks/av/media/utils/LimitProcessMemory.cpp
void limitProcessMemory(const char *property, size_t numberOfBytes,
                        size_t percentageOfTotalMem) {
    // 获取系统每一页内存的大小(通常是 4KB)
    long pageSize = sysconf(_SC_PAGESIZE);
    // 获取系统总的物理内存页数
    long numPages = sysconf(_SC_PHYS_PAGES);
    // 默认初始化为无限制 (SIZE_MAX)
    size_t maxMem = SIZE_MAX;

    if (pageSize > 0 && numPages > 0) {
        if (size_t(numPages) < SIZE_MAX / size_t(pageSize)) {
            // / 设备总物理内存(字节)
            maxMem = size_t(numPages) * size_t(pageSize);
        }
        // 限制百分比不能超过 100%
        if (percentageOfTotalMem > 100) {
            percentageOfTotalMem = 100;
        }
        
        // 计算出百分比对应的物理内存大小
        maxMem = maxMem / 100 * percentageOfTotalMem;
        
        // 取"百分比计算值"和"传入的硬性上限值"中较小的那一个作为最终限制
        if (numberOfBytes < maxMem) {
            maxMem = numberOfBytes;
        }
        ALOGV("requested limit: %zu", maxMem);
    } else {
        ALOGW("couldn't determine total RAM");
    }

    // 尝试读取指定的系统属性(如 ro.audio.maxmem)
    // 如果开发者或厂商配置了这个属性,那么使用属性值直接覆盖刚刚计算出来的 maxMem。
    // 这为调试和不同硬件平台的定制留下了后门。
    int64_t propVal = property_get_int64(property, maxMem);
    if (propVal > 0 && uint64_t(propVal) <= SIZE_MAX) {
        maxMem = propVal;
    }

    // If Scudo is in use, enforce the hard RSS limit (in MB).
    // 检查当前系统是否链接了 Scudo 内存分配器(Android 默认的现代安全内存分配器)
    if (maxMem != SIZE_MAX && &__scudo_set_rss_limit != 0) {
      // 将字节数向右位移 20 位(相当于除以 1024 * 1024),将单位从 Bytes 
      // 转换为 Megabytes (MB)
      // RSS 指常驻集大小(物理内存)。第二个参数 1 表示硬性上限,超限会被 Scudo 直接 OOM 终止,然后 return。
      __scudo_set_rss_limit(maxMem >> 20, 1);
      ALOGV("Scudo hard RSS limit set to %zu MB", maxMem >> 20);
      return;
    }
		// M_SET_ALLOCATION_LIMIT_BYTES 用于限制堆内存分配总量;超过 maxMem 后,
		// 后续 malloc/new 失败(返回 nullptr 或抛异常)。
    if (!android_mallopt(M_SET_ALLOCATION_LIMIT_BYTES, &maxMem,
                         sizeof(maxMem))) {
      ALOGW("couldn't set allocation limit");
    }
}

总结

  1. 首先限制了当前进程可以申请的最大物理内存
  2. 创建了AudioFlinger, AudioPolicyService,并将这两个实例保存在AudioSystem中.之后再将这两个实例分别注册为名为media.audio_flingermedia.audio_policy服务.
  3. 根据是否支持mmap,有条件的创建AAudioService
相关推荐
brucelee1867 小时前
Docker 运行 Android 模拟器
android·docker·容器
私人珍藏库7 小时前
[Android] 小柚市场app v2.3.0.8安卓版TV版
android
HackTorjan7 小时前
MySQL高可用架构设计与最佳实践
android·人工智能·mysql·adb·自动化
Gary Studio7 小时前
自定义 Android 系统服务与 HAL 交互全流程指南
android·交互
JMchen1238 小时前
NDK新趋势——Rust与Android深度集成实战
android·开发语言·rust·jni·内存安全·android ndk·移动端性能
凡情8 小时前
android隐私合规检测
android·unity
私人珍藏库8 小时前
[Android] 自动连点器max1.0
android·app·工具·软件·多功能
zhangphil8 小时前
Android Page3与Flow分页查媒体数据库展示宫格图片列表,Kotlin
android·kotlin
xxjj998a8 小时前
Laravel4.x:PHP开发新纪元
android·数据库