Android 8.0 VTS 测试 FAIL 项解决记录

本文是转自https://zhuanlan.zhihu.com/p/28426650

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

 

1. 前言

  这篇文章所记录的是我自己在 Amlogic 平台上碰到的 Android Vendor Test Suite (VTS) 测试问题,所以下文中的各 FAIL 项不一定包含了所有的问题类型。但分析 VTS 问题的思路应该是类似的,希望这篇文章能给各位工程师朋友们带来启发。

  这篇文章将不定期更新。

 

2. 准备工作

  要解决 VTS 问题,我们必须先准备好 4 件东西:

  • VTS 测试报告。该文件相对于 VTS 测试工具包的路径是 android-vts/results/<测试日期>/,文件名是 test_result.xml,需要用 Edge 或者 Firefox 浏览器查看。
  • VTS 测试日志。该文件相对于 VTS 测试工具包的路径是 android-vts/logs/<测试日期>/inv_xxxxxx/,文件名是 device_logcat_test_xxxxxx.txt.gz,需要用 gzip 命令解压后查看。
  • VTS 测试用例源码。通过阅读测试用例源码,我们可以了解到每个测试项的意图和具体执行的操作。
  • HAL 模块源码。因为 VTS 测试是针对 HAL 进行的,我们需要阅读 HAL 模块的源码来追踪 HAL 模块对测试用例的响应,并修改 HAL 源码来解决 VTS 测试的失败项问题。

3. VTS 失败项及解决方法

  得到 VTS 测试报告后,也许我们会发现报告中的 FAIL 项非常多(比如我负责的 Audio 模块就有 1600 多项测试失败),这个时候千万不要慌张,要保持一个平常心,沉下心来观察哪些失败项是属于同一种原因的,优先解决数量更多的一类测试失败项。 
  VTS 工具对测试成功/失败的判定是根据变量或返回值是否符合预期进行的,所以我一般按照下面的步骤解决 VTS 问题:

  • 是什么变量(返回值)导致失败?
  • 变量(返回值)在哪个函数中产生?
  • 什么因素会影响这个变量(返回值)的值?

  如果一个 VTS 测试失败项的上方 3 个问题你都能回答上来,那么这个 VTS 问题就很容易解决。下面我们按照这个过程一起来看看我碰到和解决过的具体的 VTS 问题吧:

 

3.1 错误注入测试

(本来这是个表格,但我发现知乎不支持表格,所以只好用文字引用分段说明了)

测试项:
HidlHalGTest#RecommendedOutputStreamConfigSupport
/OutputStreamTest.AddNonExistingEffect
/0__24000_OUT_STEREO_64bit
测试结果:
fail
详细信息:
hardware/interfaces/audio/2.0/vts/functional
/AudioPrimaryHidlHalTest.cpp:602
Expected success: res
Actual: INVALID_ARGUMENTS (0x2)

  首先我们要看具体的失败信息,可以发现该项测试失败是 res 这个变量的值与期望值不符导致的。 VTS 期望的结果是 success,但实际得到 res 的值却是 INVALID_ARGUMENTS。 
  然后我们需要搞清楚什么情况才是 success。我们看到详细信息中给出了测试用例失败的位置,在 AudioPrimaryHidlHalTest.cpp 文件的第 602 行,那么自然需要去看一看究竟。相关代码段如下:

template <class Open>
    void testOpen(Open openStream, const AudioConfig& config) {
        // FIXME: Open a stream without an IOHandle
        //        This is not required to be accepted by hal implementations
        AudioIoHandle ioHandle =
            (AudioIoHandle)AudioHandleConsts::AUDIO_IO_HANDLE_NONE;
        AudioConfig suggestedConfig{};
        ASSERT_OK(openStream(ioHandle, config,
                             returnIn(res, stream, suggestedConfig)));

        // TODO: only allow failure for RecommendedPlaybackAudioConfig
        switch (res) {
            case Result::OK:
                ASSERT_TRUE(stream != nullptr);
                audioConfig = config;
                break;
            case Result::INVALID_ARGUMENTS:
                ASSERT_TRUE(stream == nullptr);
                AudioConfig suggestedConfigRetry;
                // Could not open stream with config, try again with the
                // suggested one
                ASSERT_OK(
                    openStream(ioHandle, suggestedConfig,
                               returnIn(res, stream, suggestedConfigRetry)));
                // This time it must succeed
                ASSERT_OK(res);    // 这一行就是失败信息中提到的第602行,它是对 res 的一个断言,期望的 res 值应为 Result::OK,也就是 0
                ASSERT_TRUE(stream != nullptr);
                audioConfig = suggestedConfig;
                break;
            default:
                FAIL() << "Invalid return status: "
                       << ::testing::PrintToString(res);
        }
        open = true;
    }

  上方测试用例的意图很明显:VTS 通过调用 HAL 中的 openStream() 方法,使用 config 变量中的参数来尝试打开一个音频输出流,如果打开失败,则使用 suggestedConfig 变量中的参数再打开一次,并且要求这次必须打开成功。openStream() 方法的返回值就存放在 res 变量中,然后通过断言 ASSERT_OK() 来判断这个返回值是否是 Result::OK,也就是 0(Result 是个枚举变量,这个值的定义在 types.hal 文件中)。如果是,那么该项测试就能够通过,否则就是不符合预期的,该项测试就会被标记为 FAIL。openStream() 方法在这里对应的是 HAL 中的 adev_open_output_stream() 函数。 
  那么为什么 openStream() 会失败呢?我们可以在 logcat日志 和 HAL源码 中找到答案。相关日志信息如下:

01-01 13:27:51.241 28069 28069 I VtsHalAudioV2_0TargetTest: [Test Case] setNonExistingParameter/0__24000_OUT_STEREO.RecommendedOutputStreamConfigSupport/OutputStreamTest BEGIN
......
01-01 13:27:51.245  4123  4269 D audio_hw_primary: select_devices(mode=0, out_device=0x2)
01-01 13:27:51.245  4123  4269 D audio_hw_primary: select_devices : hs=0 , hp=0, sp=2, hdmi=0x0,earpiece=0x0
01-01 13:27:51.245  4123  4269 D audio_hw_primary: select_devices : in_device(0x4), mic_in(0x4), headset_mic(0)

// 第一次尝试打开音频输出流
01-01 13:27:51.246  4123  4258 I audio_hw_primary: enter adev_open_output_stream(devices=0x40000000,format=0x1, ch=0x0003, SR=24000, flags=0x0)
01-01 13:27:51.246  4123  4258 I audio_hw_primary: hwsync_lpcm 0
01-01 13:27:51.246  4123  4258 E audio_hw_primary: DO not support yet!!
01-01 13:27:51.246  4123  4258 W DeviceHAL: Device 0xf2903340 open_output_stream: Invalid argument

// 第二次尝试打开音频输出流,VTS 要求这次必须打开成功
01-01 13:27:51.246  4123  4269 I audio_hw_primary: enter adev_open_output_stream(devices=0x40000000,format=0x1, ch=0x0003, SR=24000, flags=0x0)  //注意 flags=0x0
01-01 13:27:51.246  4123  4269 I audio_hw_primary: hwsync_lpcm 0
01-01 13:27:51.246  4123  4269 E audio_hw_primary: DO not support yet!!  // 失败信息
01-01 13:27:51.246  4123  4269 W DeviceHAL: Device 0xf2903340 open_output_stream: Invalid argument
01-01 13:27:51.249 28069 28069 I /data/local/tmp/binary_test_temp_HidlHalGTest/_64bit/VtsHalAudioV2_0TargetTest: Test result = 1

  通过上方展示的日志可以看到,openStream() 调用失败了并打印出 audio_hw_primary: DO not support yet!! 这句信息。这是一个很好的突破点,可以让我们方便地找到相应HAL代码的位置。相关代码位于 hardware/amlogic/audio/audio_hw.c 中,其中关键代码如下:

static int adev_open_output_stream(struct audio_hw_device *dev,
                                   audio_io_handle_t handle __unused,
                                   audio_devices_t devices,
                                   audio_output_flags_t flags,
                                   struct audio_config *config,
                                   struct audio_stream_out **stream_out,
                                   const char *address __unused)
{
    ......
    ALOGI("enter %s(devices=0x%04x,format=%#x, ch=0x%04x, SR=%d, flags=0x%x)", __FUNCTION__, devices,
          config->format, config->channel_mask, config->sample_rate, flags);

    out = (struct aml_stream_out *)calloc(1, sizeof(struct aml_stream_out));
    if (!out) {
        return -ENOMEM;
    }

    out->out_device = devices;

    /*-------------------------------------------------------------------------*/
    // Output flag shall not be AUDIO_OUTPUT_FLAG_NONE during HAL stage
    if (flags == AUDIO_OUTPUT_FLAG_NONE) {
        ALOGI("Amlogic_HAL - output flag is AUDIO_OUTPUT_FLAG_NONE, modify it to default value AUDIO_OUTPUT_FLAG_PRIMARY.");
        flags = AUDIO_OUTPUT_FLAG_PRIMARY;
    }
    /*-------------------------------------------------------------------------*/
    out->flags = flags;
    ......
    //hwsync with LPCM still goes to out_write_legacy
    hwsync_lpcm = (flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC && config->sample_rate <= 48000 && audio_is_linear_pcm(config->format));
    ALOGI("hwsync_lpcm %d\n", hwsync_lpcm);
    if (flags & AUDIO_OUTPUT_FLAG_PRIMARY || hwsync_lpcm) {
        ......
    } else if (flags & AUDIO_OUTPUT_FLAG_DIRECT) {
        ......
    } else {
        // TODO: add other cases here
        ALOGE("DO not support yet!!");  // 如果 flags 既不是 AUDIO_OUTPUT_FLAG_PRIMARY 又不是 AUDIO_OUTPUT_FLAG_DIRECT 就会打印这条消息
        return -EINVAL;  // 直接返回错误码退出函数
    }

    ......
    LOGFUNC("**leave %s(devices=0x%04x,format=%#x, ch=0x%04x, SR=%d)", __FUNCTION__, devices,
            config->format, config->channel_mask, config->sample_rate);

    ......
    return 0;

err_open:
    free(out);
    *stream_out = NULL;
    return ret;
}

  上方代码中 2 条 /*------*/ 之间的部分是我为了修复这个 VTS 失败项而添加的代码,原本的 HAL 源码中是没有的,我们先不要看这些新增的代码。 
  回忆一下之前贴出的日志中看到的 adev_open_output_stream() 函数接收到的参数,其中 flags=0x0,也就是 flags = AUDIO_OUTPUT_FLAG_NONE,正常情况下 HAL 不会对这种情形进行处理,而是直接打印错误消息并返回 -EINVAL。之所以 HAL 不去处理,是因为在 Android 7.0 及之前版本的系统中,AudioPolicyService 会负责为各个音频播放场景选择正确的AUDIO_OUTPUT_FLAG,再将其传递给 AudioFlinger 并以此执行 openOutputStream() 操作打开相应的音频输出流。通常来说,AudioPolicyService 都会为各个场景选取出适当的 flag,即便没有合适的 flag,也会使用默认 的AUDIO_OUTPUT_FLAG_PRIMARY,而不会出现 AUDIO_OUTPUT_FLAG_NONE 的情况。但根据 Project Treble 的要求,在 Android 8.0 及之后版本的系统中需要将 HAL 与 Framework 完全分离,以做到任意 Framework 可搭配任意 HAL,我们不能保证 Framework 一定能给 HAL 传递正确的值。 
  要解决这个 VTS 问题,我们只需要为 HAL 的 adev_open_output_stream() 函数添加上对 AUDIO_OUTPUT_FLAG_NONE 的判断和处理就可以了,也就是上方代码中 2 条 /*------*/ 之间的代码,这种修改方式会将所有的 AUDIO_OUTPUT_FLAG_NONE 视作 AUDIO_OUTPUT_FLAG_PRIMARY 进行处理。当然,你也可以在打印 DO not support!! 信息的那个 else 分支中添加对这种情况进行单独处理的代码。。

 

3.2 执行成功但返回值不符合 VTS 规范

(本来这是个表格,但我发现知乎不支持表格,所以只好用文字引用分段说明了)

测试项:
HidlHalGTest#AudioPrimaryHidlTest.setScreenState_64bit
测试结果:
fail
详细信息:
hardware/interfaces/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp:506
Value of: result
Actual: INVALID_ARGUMENTS (0x2)
Expected one of: okOrNotSupported
Which is: { OK (0x0), NOT_SUPPORTED (0x4) }

  我们继续按照最开始提到的 3 个步骤来分析。首先看到是因为 result 变量的值不符合期望,所以该项测试没有通过,对应的测试用例失败位置在 AudioPrimaryHidlHalTest.cpp 第 506 行。然后我们根据这个信息去查看测试用例代码,发现失败时调用的是 setScreenState() 这个函数,相关代码段如下:

//
/// setScreenState ///
//

TEST_F(AudioPrimaryHidlTest, setScreenState) {
    doc::test("Check that the hal can receive the screen state");
    for (bool turnedOn : {false, true, true, false, false}) {
        auto ret = device->setScreenState(turnedOn);  // 调用 device->setScreenState()方法对屏幕状态进行设置
        ASSERT_IS_OK(ret);
        Result result = ret;
        auto okOrNotSupported = {Result::OK, Result::NOT_SUPPORTED};
        ASSERT_RESULT(okOrNotSupported, result);    // 这里就是第 506 行,可以看到是对 result 的断言失败了
    }
}

  可以很清楚地看到,测试用例调用了 HAL 中的 setScreenState() 接口,执行结果存放在 ret 中,再赋值给 result 变量进行判断。只有当这个返回值是 Result::OK 或者 Result::NOT_SUPPORTED 才能通过该项测试。继续查看这项测试所对应的 Logcat 日志,如下:

01-01 13:24:25.904 22679 22679 I VtsHalAudioV2_0TargetTest: [Test Case] setScreenState.AudioPrimaryHidlTest BEGIN
......
01-01 13:24:25.908  4123  4123 D audio_hw_primary: select_devices(mode=0, out_device=0x2)
01-01 13:24:25.908  4123  4123 D audio_hw_primary: select_devices : hs=0 , hp=0, sp=2, hdmi=0x0,earpiece=0x0
01-01 13:24:25.908  4123  4123 D audio_hw_primary: select_devices : in_device(0x4), mic_in(0x4), headset_mic(0)
// 测试用例实际调用了 adev_set_parameters() 函数
01-01 13:24:25.909  4123  4269 D audio_hw_primary: adev_set_parameters(0xf3f4d6c0, screen_state=off)
01-01 13:24:25.909 22679 22679 I VtsHalAudioV2_0TargetTest: [Test Case] setScreenState.AudioPrimaryHidlTest END
01-01 13:24:25.911 22679 22679 I /data/local/tmp/binary_test_temp_HidlHalGTest/_64bit/VtsHalAudioV2_0TargetTest: Test result = 1

  最后我们去到 HAL 层函数adev_set_parameters()进行查看,相应代码如下:

static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
{
    LOGFUNC("%s(%p, %s)", __FUNCTION__, dev, kvpairs);

    struct aml_audio_device *adev = (struct aml_audio_device *)dev;
    struct str_parms *parms;
    char *str;
    char value[32];
    int ret;
    parms = str_parms_create_str(kvpairs);
    ret = str_parms_get_str(parms, "screen_state", value, sizeof(value));
    if (ret >= 0) {
        if (strcmp(value, AUDIO_PARAMETER_VALUE_ON) == 0) {
            adev->low_power = false;
        } else {
            adev->low_power = true;
        }
    }
    str_parms_destroy(parms);

    /*------------------------------------------------------------------------*/
    // VTS regards 0 as success, so if we setting parameter successfully,
    // zero should be returned instead of data length.
    // To pass VTS test, ret must be Result::OK (0) or Result::NOT_SUPPORTED (4).
    if (ret > 0) {
        ALOGI("Amlogic_HAL - %s: return 0 instead of length of data be copied.", __FUNCTION__);
        ret = 0;
    } else if (ret < 0) {
        ALOGI("Amlogic_HAL - %s: return Result::NOT_SUPPORTED (4) instead of other error code.", __FUNCTION__);
        ret = 4;
    }
    /*------------------------------------------------------------------------*/

    return ret;
}

  同样,在 2 条 /*------*/ 之间的代码是我为了修复这个 VTS 测试失败项而添加的代码,我们先不去看它们。 adev_set_parameters() 函数会对传入的键值对 kvpairs 进行检查,并取出指定键所对应的值,将其拷贝到value变量中,被拷贝的字节数将作为返回值存放在 ret 变量中。如果kvpairs中没有本次操作所需要的内容,那么 ret 的值将是一个负数。最终 ret 将作为adev_set_parameters() 函数的返回值被返回到 VTS 进行测试成功判断,但 VTS 只接受 Result::OK 和 Result::NOT_SUPPORTED 作为返回值,否则将视为该测试项测试失败。 
  解决这个 VTS 问题的方法就是对最终的ret变量进行处理,返回 VTS 可接受的返回值。

 

3.3 缺少必需的 HAL 接口

(注意:原则上我们不能去修改 AOSP 的接口,所以这一项的修改方式并不够标准)

(本来这是个表格,但我发现知乎不支持表格,所以只好用文字引用分段说明了)

测试项:
HidlHalGTest#RecommendedOutputStreamConfigSupport
/OutputStreamTest.SupportedChannelMask
/0__24000_OUT_STEREO_64bit
测试结果:
fail
详细信息:
hardware/interfaces/audio/2.0/vts
/functional/AudioPrimaryHidlHalTest.cpp:795 
Expected success: (stream->*setter)(capability) 
Actual: INVALID_ARGUMENTS (0x2)

  这个 VTS 测试项失败的原因是 (stream->*setter)(capability) 的返回值 INVALID_ARGUMENTS(0x2) 与期望的结果不符。有了解决之前 2 个问题的经验,我们分析起问题来已经轻车熟路了,直接查看 AudioPrimaryHidlHalTest.cpp 第 795 行相关的测试用例代码,如下:

template <class Property, class CapabilityGetter, class Getter, class Setter>
static void testCapabilityGetter(const string& name, IStream* stream,
                                 Property currentValue,
                                 CapabilityGetter capablityGetter,
                                 Getter getter, Setter setter) {
    hidl_vec<Property> capabilities;
    ASSERT_OK((stream->*capablityGetter)(returnIn(capabilities)));
    if (capabilities.size() == 0) {
        // The default hal should probably return a NOT_SUPPORTED if the hal
        // does not expose
        // capability retrieval. For now it returns an empty list if not
        // implemented
        doc::partialTest(name + " is not supported");
        return;
    };
    // TODO: This code has never been tested on a hal that supports
    // getSupportedSampleRates
    EXPECT_NE(std::find(capabilities.begin(), capabilities.end(), currentValue),
              capabilities.end())
        << "current " << name << " is not in the list of the supported ones "
        << toString(capabilities);

    // Check that all declared supported values are indeed supported
    for (auto capability : capabilities) {
        ASSERT_OK((stream->*setter)(capability));    // 这里就是错误详情里提到的第 795 行
        ASSERT_EQ(capability, extract((stream->*getter)()));
    }
}

  这是个模板函数,而 stream->*setter 则是个函数指针,具体是指向哪个函数需要视情况而定。我们再仔细观察一下测试项名称,发现其中有 OutputStreamTest.SupportedChannelMask 这样的说明,表示这项测试是和声道数相关的。然后我们再在测试用例代码中找到使用这个模板函数的地方,如下:

TEST_IO_STREAM(SupportedChannelMask,
               "Check that the stream channel mask is declared as supported",
               testCapabilityGetter("getSupportedChannelMask", stream.get(),
                                    extract(stream->getChannelMask()),
                                    &IStream::getSupportedChannelMasks,
                                    &IStream::getChannelMask,
                                    &IStream::setChannelMask))    // *setter 指向了 setChannelMask

  找到了模板函数的引用位置,我们可以清楚地看到 stream->*setter 指向了 setChannelMask 操作。按照之前的分析步骤,接下来我们应该查看这个测试用例在 HAL 中所对应的具体函数实现,但是我发现 HAL 代码中并没有 out_set_channel_mask() 的实现,甚至连函数指针都没有定义。因此,要想通过这项 VTS 测试,我们必须在 HAL 添加上 out_set_channel_mask() 函数的声明及实现。具体的方法是在 hardware/libhardware/include/hardware/audio.h 里的 audio_stream 结构体中添加上函数指针声明,然后再在 audio_hw.c 中进行实现。如下:


// hardware/libhardware/include/hardware/audio.h

/* common audio stream parameters and operations */
struct audio_stream {
    ......
    uint32_t (*get_sample_rate)(const struct audio_stream *stream);
    ......
    int (*set_sample_rate)(struct audio_stream *stream, uint32_t rate);
    ......
    size_t (*get_buffer_size)(const struct audio_stream *stream);
    ......
    audio_channel_mask_t (*get_channels)(const struct audio_stream *stream);
    /*-------------------------------------------------------------------*/
    /**
     * Add out_set_channels() function for passing VTS test.
     */
    int (*set_channels)(struct audio_stream *stream, audio_channel_mask_t channel_mask);
    /*-------------------------------------------------------------------*/
    ......
    audio_format_t (*get_format)(const struct audio_stream *stream);
    ......
    int (*set_format)(struct audio_stream *stream, audio_format_t format);
    ......
};
typedef struct audio_stream audio_stream_t;



// hardware/amlogic/audio/audio_hw.c


/*-----------------------------------------------------------------------*/
// Add out_set_channel_mask() function for passing VTS test
static int out_set_channel_mask(struct audio_stream *stream __unused, audio_channel_mask_t channel_mask __unused)
{
    ALOGE("Amlogic_HAL - %s: workaround for passing VTS.", __FUNCTION__);
    return 0;
}
/*-----------------------------------------------------------------------*/

......

static int adev_open_output_stream(struct audio_hw_device *dev,
                                   audio_io_handle_t handle __unused,
                                   audio_devices_t devices,
                                   audio_output_flags_t flags,
                                   struct audio_config *config,
                                   struct audio_stream_out **stream_out,
                                   const char *address __unused)
{
    ......
    if (flags & AUDIO_OUTPUT_FLAG_PRIMARY || hwsync_lpcm) {
        ......
    } else if (flags & AUDIO_OUTPUT_FLAG_DIRECT) {
        ......
    } else {
        ......
    }

    out->stream.common.get_sample_rate = out_get_sample_rate;
    out->stream.common.set_sample_rate = out_set_sample_rate;
    out->stream.common.get_buffer_size = out_get_buffer_size;
    out->stream.common.set_format = out_set_format;
    /*-------------------------------------------------------------*/
    // add .set_channel_mask handler for passing VTS test
    out->stream.common.set_channels = out_set_channel_mask;
    /*-------------------------------------------------------------*/
    //out->stream.common.standby = out_standby;
    out->stream.common.dump = out_dump;
    out->stream.common.set_parameters = out_set_parameters;
    out->stream.common.get_parameters = out_get_parameters;
    out->stream.common.add_audio_effect = out_add_audio_effect;
    out->stream.common.remove_audio_effect = out_remove_audio_effect;
    out->stream.get_latency = out_get_latency;
    out->stream.set_volume = out_set_volume;
    out->stream.get_render_position = out_get_render_position;
    out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
    out->stream.get_presentation_position = out_get_presentation_position;
    out->stream.pause = out_pause;
    out->stream.resume = out_resume;
    out->stream.flush = out_flush;
    ......
    return 0;

err_open:
    free(out);
    *stream_out = NULL;
    return ret;
}

  上方展示的代码片段里 2 条 /*------*/ 之间的内容就是为了修复这个 VTS 问题而添加的。

 

3.4 缺少对要求参数的处理

(本来这是个表格,但我发现知乎不支持表格,所以只好用文字引用分段说明了)

测试项:
HidlHalGTest#RecommendedOutputStreamConfigSupport
/OutputStreamTest.SupportedFormat
/0__24000_OUT_STEREO_64bit
测试结果:
fail
详细信息:
hardware/interfaces/audio/2.0
/vts/functional/AudioPrimaryHidlHalTest.cpp:796
Value of: extract((stream->*getter)())
Actual: 4-byte object <01-00 00-00>
Expected: capability
Which is: 4-byte object <00-00 00-0A>

  从测试项的名称来看,这项测试的对象是 AudioFormat。查看测试用例源码第 796 行发现是获取到的音频格式与设置的音频格式不一致导致了测试失败。如下:

template <class Property, class CapabilityGetter, class Getter, class Setter>
static void testCapabilityGetter(const string& name, IStream* stream,
                                 Property currentValue,
                                 CapabilityGetter capablityGetter,
                                 Getter getter, Setter setter) {
    hidl_vec<Property> capabilities;
    ASSERT_OK((stream->*capablityGetter)(returnIn(capabilities)));
    if (capabilities.size() == 0) {
        // The default hal should probably return a NOT_SUPPORTED if the hal
        // does not expose
        // capability retrieval. For now it returns an empty list if not
        // implemented
        doc::partialTest(name + " is not supported");
        return;
    };
    // TODO: This code has never been tested on a hal that supports
    // getSupportedSampleRates
    EXPECT_NE(std::find(capabilities.begin(), capabilities.end(), currentValue),
              capabilities.end())
        << "current " << name << " is not in the list of the supported ones "
        << toString(capabilities);

    // Check that all declared supported values are indeed supported
    for (auto capability : capabilities) {
        ASSERT_OK((stream->*setter)(capability));
        ASSERT_EQ(capability, extract((stream->*getter)()));    // 这就是测试报告中提到的第 796 行
    }
}

  根据之前排查 VTS 问题的经验,此处的 stream->*setter() 应该具体对应的是 out_set_parameters() 或 out_set_format() 函数。然后通过测试日志中的下面这条打印信息:

01-01 00:03:20.002  3156  3307 D audio_hw_primary: out_set_parameters(kvpairs(format=1), out_device=0x2)

  我们可以确定其具体对应的是 out_set_parameters(),但是在该函数中却没有对传入的键值对进行关键字 format 进行检测的代码。传入的 AudioFormat 参数并没有被真正写入到 HAL,这就是问题所在。
  要解决这个问题,我们需要为 AudioFormat 添加上相应的检测和处理代码,如下:

static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
    ......
    LOGFUNC("%s(kvpairs(%s), out_device=%#x)", __FUNCTION__, kvpairs, adev->out_device);
    parms = str_parms_create_str(kvpairs);
    ......
    /*-------------------------------------------------------------*/
    // Detect and set AUDIO_PARAMETER_STREAM_FORMAT for passing VTS
    audio_format_t fmt = 0;
    ret = str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_FORMAT, &fmt);
    if (ret >= 0) {
        if (fmt > 0) {
            struct pcm_config *config = &out->config;
            ALOGI("audio hw sampling_rate change from %d to %d \n", config->format, fmt);
            config->format = fmt;
            pthread_mutex_lock(&adev->lock);
            pthread_mutex_lock(&out->lock);
            if (!out->standby) {
                standy_func(out);
                startup_func(out);
                out->standby = 0;
            }
            // set hal_format to fmt for passing VTS
            ALOGV("Amlogic_HAL - %s: set format to hal_format. fmt = %d", __FUNCTION__, fmt);
            out->hal_format = fmt;
            pthread_mutex_unlock(&adev->lock);
            pthread_mutex_unlock(&out->lock);
        }

        // We shall return Result::OK, which is 0, if parameter is set successfully,
        // or we can not pass VTS test.
        ALOGV("Amlogic_HAL - %s: change ret value to 0 in order to pass VTS test.", __FUNCTION__);
        ret = 0;

        goto exit;
    }
    /*-------------------------------------------------------------*/
    ......

  为 out_set_parameters() 函数添加上如上所示 2 条 /*------*/ 之间的代码后,该 VTS 测试项就可以通过了。

 

3.5 其它相关问题

  可能我们会在测试日志中发现下面这样的记录:

01-01 00:14:56.848  3093  3093 W /system/bin/hwservicemanager: getTransportFromManifest: Cannot find entry android.hardware.audio@2.0::IDevicesFactory in either framework or device manifest, using default transport.
......
01-01 00:14:56.876  3093  3093 W /system/bin/hwservicemanager: getTransportFromManifest: Cannot find entry android.hardware.audio.effect@2.0::IEffectsFactory in either framework or device manifest, using default transport.

  这是由于我们没有在 manifest.xml 文件中为模块添加相应的 hwbinder 描述导致的。这个文件一般在 device/<vendorName>/<productName>/ 目录下,比如在我所使用的平台上,该文件位于 device/amlogic/common/ 目录下。在 manifest.xml 文件中为模块添加上相应的描述即可消除这条警告消息,如下:

<manifest version="1.0" type="device">
    ......
    <hal format="hidl" optional="false">
        <name>android.hardware.audio</name>
        <transport>hwbinder</transport>
        <version>2.0</version>
        <interface>
            <name>IDevicesFactory</name>
            <instance>default</instance>
        </interface>
    </hal>
    <hal format="hidl" optional="false">
        <name>android.hardware.audio.effect</name>
        <transport>hwbinder</transport>
        <version>2.0</version>
        <interface>
            <name>IEffectsFactory</name>
            <instance>default</instance>
        </interface>
    </hal>
</manifest>

  其中 optional 属性的值根据 hardware/interfaces/compatibility_matrix.xml 文件中对模块的 optional 属性描述来设置。在我所使用平台上该文件中关于 Audio 模块的描述如下:

<compatibility-matrix version="1.0" type="framework">
    <hal format="hidl" optional="false">
        <name>android.hardware.audio</name>
        <version>2.0</version>
        <interface>
            <name>IDevicesFactory</name>
            <instance>default</instance>
        </interface>
    </hal>
    <hal format="hidl" optional="false">
        <name>android.hardware.audio.effect</name>
        <version>2.0</version>
        <interface>
            <name>IEffectsFactory</name>
            <instance>default</instance>
        </interface>
    </hal>
    ......
</compatibility-matrix>
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值