在 Android 中使用自定义函数接口替换 Frameworks 中的默认函数接口

1,085 阅读2分钟
原文链接: zhuanlan.zhihu.com

注意:本文基于 Android 8.0 进行分析。


1. 前言

  自 Android 8.0 开始,为了实现 Project Treble 对更快完成系统升级的预期,Google 严格要求芯片 vendor 厂商不能再修改 AOSP 的 Frameworks 代码,这使得我们需要将之前在 Framework 中的一些改动移到别处进行实现。
  以我所负责的 Audio 模块为例,就有类似的修改。为了便于以后参考但又不造成泄密,此处只对思路作简单地记录。(实际上这种 override 的做法还是很常见的,并不是什么技术机密,实现起来也不复杂)


2. 正文

  首先我们要确认要进行替换的函数接口是哪个。
  Frameworks 中可以进行替换的接口均以 extern "C" 关键字进行修饰,表明这些函数在其它模块中有被调用。这类接口我们在 frameworks/av/services/ 目录下可以找到比较多,比如有以下这些:

extern "C" AudioPolicyInterface* createAudioPolicyManager(...){...}
extern "C" void destroyAudioPolicyManager(...){...}
extern "C" {
    static void camera_device_status_change(...){...}
    static void torch_mode_status_change(...){...}
}

  在确认了要替换的函数之后,我们需要查看对应的 makefile 文件,找到这个函数是被编译到了哪个库文件中。还是以我自己负责的 Audio 模块为例,我要替换的函数是 createAudioPolicyManager() ,在源文件 frameworks/av/services/audiopolicy/manager/AudioPolicyFactory.cpp 中,再找到对应的 Android.mk 文件,可以看到这个函数被编译进了 libaudiopolicymanager.so 中。具体定义如下:

......
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
    manager/AudioPolicyFactory.cpp  # 函数所在源文件

LOCAL_SHARED_LIBRARIES := \
    libaudiopolicymanagerdefault

LOCAL_STATIC_LIBRARIES := \
    libaudiopolicycomponents

LOCAL_C_INCLUDES += \
    $(TOPDIR)frameworks/av/services/audiopolicy/common/include \
    $(TOPDIR)frameworks/av/services/audiopolicy/engine/interface

LOCAL_CFLAGS := -Wall -Werror

LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)

LOCAL_MODULE:= libaudiopolicymanager  # 对应的库文件名

include $(BUILD_SHARED_LIBRARY)

......

  至此,我们对目标函数的定位就完成了。接下来我们要编写用于替换的自定义函数,并让系统在编译时不去处理这个默认的函数,转而编译我们的自定义函数,从而实现对 createAudioPolicyManager() 这个函数的 override。注意,因为是对函数进行替换,所以函数名必须和默认的是一样的,我们只对函数内容进行修改。
  自定义的 createAudioPolicyManager() 函数位于 hardware/<vendorName>/audio/ 目录下的 xxxAudioPolicyManager.cpp 文件中,部分内容如下:

namespace android {

extern "C" AudioPolicyInterface* createAudioPolicyManager(
        AudioPolicyClientInterface *clientInterface)
{
    // 自定义的 createAudioPolicyManager() 函数不再返回系统默认的
    // AudioPolicyManager 对象,而是返回自定义的 xxxAudioPolicyManager 对象
    return new xxxAudioPolicyManager(clientInterface);
}

extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface)
{
    delete interface;
}

// 1. 为了返回 xxxAudioPolicyManager 对象,我们需要先按照需求定义
// 相应的 xxxAudioPolicyManager 类。
// 2. 实际上这个构造函数只是简单的构建了一个旧的 AudioPolicyManager,
// 再初始化了一个我们在新构建的类中自定义的变量 mCustomVar
xxxAudioPolicyManager::xxxAudioPolicyManager(
        AudioPolicyClientInterface *clientInterface)
    : AudioPolicyManager(clientInterface), mCustomVar(false)
{
}

status_t xxxAudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
                                                      audio_policy_dev_state_t state,
                                                      const char *device_address,
                                                      const char *device_name)
{
    audio_devices_t tmp = AUDIO_DEVICE_NONE;
    // 当选中设备为 BackMic 时进行单独处理
    if (device == AUDIO_DEVICE_IN_BACK_MIC && device_address) {
        AudioParameter parameters = AudioParameter(String8(device_address));
        int customValue;
        if (parameters.getInt(String8("custom"), customValue) == OK) {
            mCustomVar = customValue != 0;
        }
    }
    status_t ret = 0;
    // 如果选中设备不是 BackMic 则再调用默认
    // AudioPolicyManager 对象中默认的 setDeviceConnectionState 方法
    // 进行处理。
    // 否则,跳过设置操作,直接返回 0
    if (device != AUDIO_DEVICE_IN_BACK_MIC) {
      ret = AudioPolicyManager::setDeviceConnectionState(
                    device, state, device_address,device_name);
    }
    return ret;
}

audio_devices_t xxxAudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource)
{
    uint32_t device = AUDIO_DEVICE_NONE;
    // 如果当前音频输入源是 HOTWORD 那么进行单独处理
    if (inputSource == AUDIO_SOURCE_HOTWORD) {
      audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() &
                                                ~AUDIO_DEVICE_BIT_IN;
        // 如果 BackMic 设备可用,则将 Hotword 场景下
        // 的音频输入设备选择为 BackMic
      if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC &&
            mForceSubmixInputSelection) {
          device = AUDIO_DEVICE_IN_BACK_MIC;
      }
      else
          // 如果 BackMic 设备不可用,则调用默认 AudioPolicyManager 对象中
          // 默认的 getDeviceForInputSource 方法获得设备
          device = AudioPolicyManager::getDeviceForInputSource(inputSource);
    }
    else
        device = AudioPolicyManager::getDeviceForInputSource(inputSource);

    return device;
}

} // namespace android over

  然后我们要将默认的函数接口屏蔽掉,这很简单,只需要在 makefile 中用一个对宏进行判断的语句将默认的源文件包裹起来就可以实现了。也就说在我这个例子中要修改 frameworks/av/services/audiopolicy/Android.mk 文件,如下:

ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1)  # 使用 USE_CUSTOM_AUDIO_POLICY 宏来进行编译选择

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
    manager/AudioPolicyFactory.cpp  # 默认函数所在源文件

LOCAL_SHARED_LIBRARIES := \
    libaudiopolicymanagerdefault

LOCAL_STATIC_LIBRARIES := \
    libaudiopolicycomponents

......

LOCAL_MODULE:= libaudiopolicymanager  # 默认函数所在库文件名

include $(BUILD_SHARED_LIBRARY)

endif

  最后将自定义的函数加入编译系统就大功告成了。因为我们自定义的函数位于 hardware/<vendorName>/audio/xxxAudioPolicyManager.cpp 文件中,所以我们在对应目录的 Android.mk 文件中添加以下内容:

ifeq ($(USE_CUSTOM_AUDIO_POLICY),1)  # 使用 USE_CUSTOM_AUDIO_POLICY 宏来进行编译选择
include $(CLEAR_VARS)

LOCAL_SRC_FILES := \
        DLGAudioPolicyManager.cpp  # 自定义函数所在源文件

LOCAL_SHARED_LIBRARIES := \
        libcutils \
        liblog \
        libutils \
        ......

LOCAL_C_INCLUDES := \
        external/tinyalsa/include \
        $(TOPDIR)frameworks/av/services/audiopolicy \
        ......

LOCAL_MODULE := libaudiopolicymanager  # 自定义函数所在库文件名,必须与默认函数所在库文件名一致

include $(BUILD_SHARED_LIBRARY)
endif # USE_CUSTOM_AUDIO_POLICY

  至此,为了对 createAudioPolicyManager() 函数进行 override 所要做的修改就完成了。实际上在上方的例子中对 setDeviceConnectionState() 和 getDeviceForInputSource() 函数也进行了 override。
  最后,编译 libaudiopolicymanager.so,再 push 到设备上进行验证,可以看到我们的修改是有效的。


3、参考链接

[1] 《C++项目中的extern “C” {}》