blob: a2912e960034cf5293096d8adddbe05078e4d773 [file]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "chre/common.h"
#include "inc/test_util.h"
#include "test_base.h"
#include <gtest/gtest.h>
#include <cstdint>
#include "chre/core/event_loop_manager.h"
#include "chre/core/settings.h"
#include "chre/platform/fatal_error.h"
#include "chre/platform/linux/pal_ble.h"
#include "chre/util/dynamic_vector.h"
#include "chre_api/chre/ble.h"
#include "chre_api/chre/user_settings.h"
#include "test_util.h"
namespace chre {
namespace {
/**
* This test verifies that a nanoapp can query for BLE capabilities and filter
* capabilities. Note that a nanoapp does not require BLE permissions to use
* these APIs.
*/
TEST_F(TestBase, BleCapabilitiesTest) {
CREATE_CHRE_TEST_EVENT(GET_CAPABILITIES, 0);
CREATE_CHRE_TEST_EVENT(GET_FILTER_CAPABILITIES, 1);
class App : public TestNanoapp {
public:
App()
: TestNanoapp(
TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_WIFI}) {}
void handleEvent(uint32_t, uint16_t eventType,
const void *eventData) override {
switch (eventType) {
case CHRE_EVENT_TEST_EVENT: {
auto event = static_cast<const TestEvent *>(eventData);
switch (event->type) {
case GET_CAPABILITIES: {
TestEventQueueSingleton::get()->pushEvent(
GET_CAPABILITIES, chreBleGetCapabilities());
break;
}
case GET_FILTER_CAPABILITIES: {
TestEventQueueSingleton::get()->pushEvent(
GET_FILTER_CAPABILITIES, chreBleGetFilterCapabilities());
break;
}
}
}
}
}
};
uint64_t appId = loadNanoapp(MakeUnique<App>());
uint32_t capabilities;
sendEventToNanoapp(appId, GET_CAPABILITIES);
waitForEvent(GET_CAPABILITIES, &capabilities);
ASSERT_EQ(capabilities, CHRE_BLE_CAPABILITIES_SCAN |
CHRE_BLE_CAPABILITIES_SCAN_RESULT_BATCHING |
CHRE_BLE_CAPABILITIES_SCAN_FILTER_BEST_EFFORT);
sendEventToNanoapp(appId, GET_FILTER_CAPABILITIES);
waitForEvent(GET_FILTER_CAPABILITIES, &capabilities);
ASSERT_EQ(capabilities, CHRE_BLE_FILTER_CAPABILITIES_RSSI |
CHRE_BLE_FILTER_CAPABILITIES_SERVICE_DATA);
}
class BleTestNanoapp : public TestNanoapp {
public:
BleTestNanoapp()
: TestNanoapp(
TestNanoappInfo{.perms = NanoappPermissions::CHRE_PERMS_BLE}) {}
bool start() override {
chreUserSettingConfigureEvents(CHRE_USER_SETTING_BLE_AVAILABLE,
true /* enable */);
return true;
}
void end() override {
chreUserSettingConfigureEvents(CHRE_USER_SETTING_BLE_AVAILABLE,
false /* enable */);
}
};
/**
* This test validates the case in which a nanoapp starts a scan, receives
* at least one advertisement event, and stops a scan.
*/
TEST_F(TestBase, BleSimpleScanTest) {
CREATE_CHRE_TEST_EVENT(START_SCAN, 0);
CREATE_CHRE_TEST_EVENT(SCAN_STARTED, 1);
CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2);
CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3);
class App : public BleTestNanoapp {
public:
void handleEvent(uint32_t, uint16_t eventType,
const void *eventData) override {
switch (eventType) {
case CHRE_EVENT_BLE_ASYNC_RESULT: {
auto *event = static_cast<const struct chreAsyncResult *>(eventData);
if (event->errorCode == CHRE_ERROR_NONE) {
uint16_t type =
(event->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN)
? SCAN_STARTED
: SCAN_STOPPED;
TestEventQueueSingleton::get()->pushEvent(type);
}
break;
}
case CHRE_EVENT_BLE_ADVERTISEMENT: {
TestEventQueueSingleton::get()->pushEvent(
CHRE_EVENT_BLE_ADVERTISEMENT);
break;
}
case CHRE_EVENT_TEST_EVENT: {
auto event = static_cast<const TestEvent *>(eventData);
switch (event->type) {
case START_SCAN: {
const bool success = chreBleStartScanAsync(
CHRE_BLE_SCAN_MODE_AGGRESSIVE, 0, nullptr);
TestEventQueueSingleton::get()->pushEvent(START_SCAN, success);
break;
}
case STOP_SCAN: {
const bool success = chreBleStopScanAsync();
TestEventQueueSingleton::get()->pushEvent(STOP_SCAN, success);
break;
}
}
break;
}
}
}
};
uint64_t appId = loadNanoapp(MakeUnique<App>());
bool success;
sendEventToNanoapp(appId, START_SCAN);
waitForEvent(START_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(SCAN_STARTED);
ASSERT_TRUE(chrePalIsBleEnabled());
waitForEvent(CHRE_EVENT_BLE_ADVERTISEMENT);
sendEventToNanoapp(appId, STOP_SCAN);
waitForEvent(STOP_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(SCAN_STOPPED);
ASSERT_FALSE(chrePalIsBleEnabled());
}
TEST_F(TestBase, BleStopScanOnUnload) {
CREATE_CHRE_TEST_EVENT(START_SCAN, 0);
CREATE_CHRE_TEST_EVENT(SCAN_STARTED, 1);
class App : public BleTestNanoapp {
public:
void handleEvent(uint32_t, uint16_t eventType,
const void *eventData) override {
switch (eventType) {
case CHRE_EVENT_BLE_ASYNC_RESULT: {
auto *event = static_cast<const struct chreAsyncResult *>(eventData);
if (event->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN &&
event->errorCode == CHRE_ERROR_NONE) {
TestEventQueueSingleton::get()->pushEvent(SCAN_STARTED);
}
break;
}
case CHRE_EVENT_TEST_EVENT: {
auto event = static_cast<const TestEvent *>(eventData);
switch (event->type) {
case START_SCAN: {
const bool success = chreBleStartScanAsync(
CHRE_BLE_SCAN_MODE_AGGRESSIVE, 0, nullptr);
TestEventQueueSingleton::get()->pushEvent(START_SCAN, success);
break;
}
}
break;
}
}
}
};
uint64_t appId = loadNanoapp(MakeUnique<App>());
bool success;
sendEventToNanoapp(appId, START_SCAN);
waitForEvent(START_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(SCAN_STARTED);
ASSERT_TRUE(chrePalIsBleEnabled());
unloadNanoapp(appId);
ASSERT_FALSE(chrePalIsBleEnabled());
}
/**
* This test validates that a nanoapp can start a scan twice and the platform
* will be enabled.
*/
TEST_F(TestBase, BleStartTwiceScanTest) {
CREATE_CHRE_TEST_EVENT(START_SCAN, 0);
CREATE_CHRE_TEST_EVENT(SCAN_STARTED, 1);
CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2);
CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3);
class App : public BleTestNanoapp {
public:
void handleEvent(uint32_t, uint16_t eventType, const void *eventData) {
switch (eventType) {
case CHRE_EVENT_BLE_ASYNC_RESULT: {
auto *event = static_cast<const struct chreAsyncResult *>(eventData);
if (event->errorCode == CHRE_ERROR_NONE) {
uint16_t type =
(event->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN)
? SCAN_STARTED
: SCAN_STOPPED;
TestEventQueueSingleton::get()->pushEvent(type);
}
break;
}
case CHRE_EVENT_BLE_ADVERTISEMENT: {
TestEventQueueSingleton::get()->pushEvent(
CHRE_EVENT_BLE_ADVERTISEMENT);
break;
}
case CHRE_EVENT_TEST_EVENT: {
auto event = static_cast<const TestEvent *>(eventData);
switch (event->type) {
case START_SCAN: {
const bool success = chreBleStartScanAsync(
CHRE_BLE_SCAN_MODE_AGGRESSIVE, 0, nullptr);
TestEventQueueSingleton::get()->pushEvent(START_SCAN, success);
break;
}
case STOP_SCAN: {
const bool success = chreBleStopScanAsync();
TestEventQueueSingleton::get()->pushEvent(STOP_SCAN, success);
break;
}
}
}
}
}
};
uint64_t appId = loadNanoapp(MakeUnique<App>());
bool success;
sendEventToNanoapp(appId, START_SCAN);
waitForEvent(START_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(SCAN_STARTED);
sendEventToNanoapp(appId, START_SCAN);
waitForEvent(START_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(SCAN_STARTED);
waitForEvent(CHRE_EVENT_BLE_ADVERTISEMENT);
sendEventToNanoapp(appId, STOP_SCAN);
waitForEvent(STOP_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(SCAN_STOPPED);
}
/**
* This test validates that a nanoapp can request to stop a scan twice without
* any ongoing scan existing. It asserts that the nanoapp did not receive any
* advertisment events because a scan was never started.
*/
TEST_F(TestBase, BleStopTwiceScanTest) {
CREATE_CHRE_TEST_EVENT(SCAN_STARTED, 1);
CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2);
CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3);
class App : public BleTestNanoapp {
public:
void handleEvent(uint32_t, uint16_t eventType,
const void *eventData) override {
switch (eventType) {
case CHRE_EVENT_BLE_ASYNC_RESULT: {
auto *event = static_cast<const struct chreAsyncResult *>(eventData);
if (event->errorCode == CHRE_ERROR_NONE) {
uint16_t type =
(event->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN)
? SCAN_STARTED
: SCAN_STOPPED;
TestEventQueueSingleton::get()->pushEvent(type);
}
break;
}
case CHRE_EVENT_BLE_ADVERTISEMENT: {
FATAL_ERROR("No advertisement expected");
break;
}
case CHRE_EVENT_TEST_EVENT: {
auto event = static_cast<const TestEvent *>(eventData);
switch (event->type) {
case STOP_SCAN: {
const bool success = chreBleStopScanAsync();
TestEventQueueSingleton::get()->pushEvent(STOP_SCAN, success);
break;
}
}
}
}
}
};
uint64_t appId = loadNanoapp(MakeUnique<App>());
bool success;
sendEventToNanoapp(appId, STOP_SCAN);
waitForEvent(STOP_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(SCAN_STOPPED);
sendEventToNanoapp(appId, STOP_SCAN);
waitForEvent(STOP_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(SCAN_STOPPED);
unloadNanoapp(appId);
}
/**
* This test verifies the following BLE settings behavior:
* 1) Nanoapp makes BLE scan request
* 2) Toggle BLE setting -> disabled
* 3) Toggle BLE setting -> enabled.
* 4) Verify things resume.
*/
TEST_F(TestBase, BleSettingChangeTest) {
CREATE_CHRE_TEST_EVENT(START_SCAN, 0);
CREATE_CHRE_TEST_EVENT(SCAN_STARTED, 1);
CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3);
class App : public BleTestNanoapp {
public:
void handleEvent(uint32_t, uint16_t eventType,
const void *eventData) override {
switch (eventType) {
case CHRE_EVENT_BLE_ASYNC_RESULT: {
auto *event = static_cast<const struct chreAsyncResult *>(eventData);
if (event->errorCode == CHRE_ERROR_NONE) {
uint16_t type =
(event->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN)
? SCAN_STARTED
: SCAN_STOPPED;
TestEventQueueSingleton::get()->pushEvent(type);
}
break;
}
case CHRE_EVENT_BLE_ADVERTISEMENT: {
TestEventQueueSingleton::get()->pushEvent(
CHRE_EVENT_BLE_ADVERTISEMENT);
break;
}
case CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE: {
auto *event =
static_cast<const chreUserSettingChangedEvent *>(eventData);
bool enabled =
(event->settingState == CHRE_USER_SETTING_STATE_ENABLED);
TestEventQueueSingleton::get()->pushEvent(
CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE, enabled);
break;
}
case CHRE_EVENT_TEST_EVENT: {
auto event = static_cast<const TestEvent *>(eventData);
switch (event->type) {
case START_SCAN: {
const bool success = chreBleStartScanAsync(
CHRE_BLE_SCAN_MODE_AGGRESSIVE, 0, nullptr);
TestEventQueueSingleton::get()->pushEvent(START_SCAN, success);
break;
}
}
}
}
}
};
uint64_t appId = loadNanoapp(MakeUnique<App>());
bool success;
sendEventToNanoapp(appId, START_SCAN);
waitForEvent(START_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(SCAN_STARTED);
waitForEvent(CHRE_EVENT_BLE_ADVERTISEMENT);
EventLoopManagerSingleton::get()->getSettingManager().postSettingChange(
Setting::BLE_AVAILABLE, false /* enabled */);
bool enabled;
waitForEvent(CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE, &enabled);
EXPECT_FALSE(enabled);
EXPECT_FALSE(
EventLoopManagerSingleton::get()->getSettingManager().getSettingEnabled(
Setting::BLE_AVAILABLE));
std::this_thread::sleep_for(std::chrono::milliseconds(100));
EXPECT_FALSE(chrePalIsBleEnabled());
EventLoopManagerSingleton::get()->getSettingManager().postSettingChange(
Setting::BLE_AVAILABLE, true /* enabled */);
waitForEvent(CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE, &enabled);
EXPECT_TRUE(enabled);
EXPECT_TRUE(
EventLoopManagerSingleton::get()->getSettingManager().getSettingEnabled(
Setting::BLE_AVAILABLE));
waitForEvent(CHRE_EVENT_BLE_ADVERTISEMENT);
EXPECT_TRUE(chrePalIsBleEnabled());
}
/**
* Test that a nanoapp receives a function disabled error if it attempts to
* start a scan when the BLE setting is disabled.
*/
TEST_F(TestBase, BleSettingDisabledStartScanTest) {
CREATE_CHRE_TEST_EVENT(START_SCAN, 0);
class App : public BleTestNanoapp {
public:
void handleEvent(uint32_t, uint16_t eventType,
const void *eventData) override {
switch (eventType) {
case CHRE_EVENT_BLE_ASYNC_RESULT: {
auto *event = static_cast<const struct chreAsyncResult *>(eventData);
if (event->errorCode == CHRE_ERROR_FUNCTION_DISABLED) {
TestEventQueueSingleton::get()->pushEvent(
CHRE_EVENT_BLE_ASYNC_RESULT);
}
break;
}
case CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE: {
auto *event =
static_cast<const chreUserSettingChangedEvent *>(eventData);
bool enabled =
(event->settingState == CHRE_USER_SETTING_STATE_ENABLED);
TestEventQueueSingleton::get()->pushEvent(
CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE, enabled);
break;
}
case CHRE_EVENT_TEST_EVENT: {
auto event = static_cast<const TestEvent *>(eventData);
switch (event->type) {
case START_SCAN: {
const bool success = chreBleStartScanAsync(
CHRE_BLE_SCAN_MODE_AGGRESSIVE, 0, nullptr);
TestEventQueueSingleton::get()->pushEvent(START_SCAN, success);
break;
}
}
}
}
}
};
uint64_t appId = loadNanoapp(MakeUnique<App>());
EventLoopManagerSingleton::get()->getSettingManager().postSettingChange(
Setting::BLE_AVAILABLE, false /* enable */);
bool enabled;
waitForEvent(CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE, &enabled);
EXPECT_FALSE(enabled);
bool success;
sendEventToNanoapp(appId, START_SCAN);
waitForEvent(START_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(CHRE_EVENT_BLE_ASYNC_RESULT);
}
/**
* Test that a nanoapp receives a success response when it attempts to stop a
* BLE scan while the BLE setting is disabled.
*/
TEST_F(TestBase, BleSettingDisabledStopScanTest) {
CREATE_CHRE_TEST_EVENT(SCAN_STARTED, 1);
CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2);
CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3);
class App : public BleTestNanoapp {
public:
void handleEvent(uint32_t, uint16_t eventType,
const void *eventData) override {
switch (eventType) {
case CHRE_EVENT_BLE_ASYNC_RESULT: {
auto *event = static_cast<const struct chreAsyncResult *>(eventData);
if (event->errorCode == CHRE_ERROR_NONE) {
uint16_t type =
(event->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN)
? SCAN_STARTED
: SCAN_STOPPED;
TestEventQueueSingleton::get()->pushEvent(type);
}
break;
}
case CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE: {
auto *event =
static_cast<const chreUserSettingChangedEvent *>(eventData);
bool enabled =
(event->settingState == CHRE_USER_SETTING_STATE_ENABLED);
TestEventQueueSingleton::get()->pushEvent(
CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE, enabled);
break;
}
case CHRE_EVENT_TEST_EVENT: {
auto event = static_cast<const TestEvent *>(eventData);
switch (event->type) {
case STOP_SCAN: {
const bool success = chreBleStopScanAsync();
TestEventQueueSingleton::get()->pushEvent(STOP_SCAN, success);
break;
}
}
}
}
}
};
uint64_t appId = loadNanoapp(MakeUnique<App>());
EventLoopManagerSingleton::get()->getSettingManager().postSettingChange(
Setting::BLE_AVAILABLE, false /* enable */);
bool enabled;
waitForEvent(CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE, &enabled);
EXPECT_FALSE(enabled);
bool success;
sendEventToNanoapp(appId, STOP_SCAN);
waitForEvent(STOP_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(SCAN_STOPPED);
}
/**
* Test that a nanoapp can read RSSI successfully.
*/
TEST_F(TestBase, BleReadRssi) {
constexpr uint16_t kConnectionHandle = 6;
constexpr uint32_t kCookie = 123;
CREATE_CHRE_TEST_EVENT(RSSI_REQUEST, 1);
CREATE_CHRE_TEST_EVENT(RSSI_REQUEST_SENT, 2);
class App : public BleTestNanoapp {
void handleEvent(uint32_t, uint16_t eventType, const void *eventData) {
switch (eventType) {
case CHRE_EVENT_BLE_RSSI_READ: {
auto *event =
static_cast<const struct chreBleReadRssiEvent *>(eventData);
if (event->result.errorCode == CHRE_ERROR_NONE) {
TestEventQueueSingleton::get()->pushEvent(CHRE_EVENT_BLE_RSSI_READ);
}
break;
}
case CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE: {
auto *event =
static_cast<const chreUserSettingChangedEvent *>(eventData);
bool enabled =
(event->settingState == CHRE_USER_SETTING_STATE_ENABLED);
TestEventQueueSingleton::get()->pushEvent(
CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE, enabled);
break;
}
case CHRE_EVENT_TEST_EVENT: {
auto event = static_cast<const TestEvent *>(eventData);
switch (event->type) {
case RSSI_REQUEST: {
const bool success =
chreBleReadRssiAsync(kConnectionHandle, (void *)kCookie);
TestEventQueueSingleton::get()->pushEvent(RSSI_REQUEST_SENT,
success);
break;
}
}
}
}
}
};
uint64_t appId = loadNanoapp(MakeUnique<App>());
EventLoopManagerSingleton::get()->getSettingManager().postSettingChange(
Setting::BLE_AVAILABLE, true /* enabled */);
bool enabled;
waitForEvent(CHRE_EVENT_SETTING_CHANGED_BLE_AVAILABLE, &enabled);
EXPECT_TRUE(enabled);
bool success;
sendEventToNanoapp(appId, RSSI_REQUEST);
waitForEvent(RSSI_REQUEST_SENT, &success);
EXPECT_TRUE(success);
waitForEvent(CHRE_EVENT_BLE_RSSI_READ);
}
/**
* This test validates that a nanoapp can start call start scan twice before
* receiving an async response. It should invalidate its original request by
* calling start scan a second time.
*/
TEST_F(TestBase, BleStartScanTwiceBeforeAsyncResponseTest) {
CREATE_CHRE_TEST_EVENT(START_SCAN, 0);
CREATE_CHRE_TEST_EVENT(SCAN_STARTED, 1);
CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2);
CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3);
struct testData {
void *cookie;
};
class App : public BleTestNanoapp {
void handleEvent(uint32_t, uint16_t eventType, const void *eventData) {
switch (eventType) {
case CHRE_EVENT_BLE_ASYNC_RESULT: {
auto *event = static_cast<const struct chreAsyncResult *>(eventData);
uint16_t type =
(event->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN)
? SCAN_STARTED
: SCAN_STOPPED;
TestEventQueueSingleton::get()->pushEvent(type, *event);
break;
}
case CHRE_EVENT_TEST_EVENT: {
auto event = static_cast<const TestEvent *>(eventData);
switch (event->type) {
case START_SCAN: {
auto data = static_cast<testData *>(event->data);
const bool success = chreBleStartScanAsyncV1_9(
CHRE_BLE_SCAN_MODE_AGGRESSIVE, 0, nullptr, data->cookie);
TestEventQueueSingleton::get()->pushEvent(START_SCAN, success);
break;
}
case STOP_SCAN: {
auto data = static_cast<testData *>(event->data);
const bool success = chreBleStopScanAsyncV1_9(data->cookie);
TestEventQueueSingleton::get()->pushEvent(STOP_SCAN, success);
break;
}
}
}
}
}
};
uint64_t appId = loadNanoapp(MakeUnique<App>());
bool success;
delayBleScanStart(true /* delay */);
testData data;
uint32_t cookieOne = 1;
data.cookie = &cookieOne;
sendEventToNanoapp(appId, START_SCAN, data);
waitForEvent(START_SCAN, &success);
EXPECT_TRUE(success);
uint32_t cookieTwo = 2;
data.cookie = &cookieTwo;
sendEventToNanoapp(appId, START_SCAN, data);
waitForEvent(START_SCAN, &success);
EXPECT_TRUE(success);
chreAsyncResult result;
waitForEvent(SCAN_STARTED, &result);
EXPECT_EQ(result.errorCode, CHRE_ERROR_OBSOLETE_REQUEST);
EXPECT_EQ(result.cookie, &cookieOne);
// Respond to the first scan request. CHRE will then attempt the next scan
// request at which point the PAL should no longer delay the response.
delayBleScanStart(false /* delay */);
EXPECT_TRUE(startBleScan());
waitForEvent(SCAN_STARTED, &result);
EXPECT_EQ(result.errorCode, CHRE_ERROR_NONE);
EXPECT_EQ(result.cookie, &cookieTwo);
sendEventToNanoapp(appId, STOP_SCAN, data);
waitForEvent(STOP_SCAN, &success);
EXPECT_TRUE(success);
waitForEvent(SCAN_STOPPED);
}
/**
* This test validates that a nanoapp can call flush only when an existing scan
* is enabled for the nanoapp. This test validates that batching will hold the
* data and flush will send the batched data and then a flush complete event.
*/
TEST_F(TestBase, BleFlush) {
CREATE_CHRE_TEST_EVENT(START_SCAN, 0);
CREATE_CHRE_TEST_EVENT(SCAN_STARTED, 1);
CREATE_CHRE_TEST_EVENT(STOP_SCAN, 2);
CREATE_CHRE_TEST_EVENT(SCAN_STOPPED, 3);
CREATE_CHRE_TEST_EVENT(CALL_FLUSH, 4);
CREATE_CHRE_TEST_EVENT(SAW_BLE_AD_AND_FLUSH_COMPLETE, 5);
class App : public BleTestNanoapp {
public:
void handleEvent(uint32_t, uint16_t eventType,
const void *eventData) override {
switch (eventType) {
case CHRE_EVENT_BLE_ASYNC_RESULT: {
auto *event = static_cast<const struct chreAsyncResult *>(eventData);
if (event->errorCode == CHRE_ERROR_NONE) {
uint16_t type =
(event->requestType == CHRE_BLE_REQUEST_TYPE_START_SCAN)
? SCAN_STARTED
: SCAN_STOPPED;
TestEventQueueSingleton::get()->pushEvent(type);
}
break;
}
case CHRE_EVENT_BLE_ADVERTISEMENT: {
mSawBleAdvertisementEvent = true;
break;
}
case CHRE_EVENT_BLE_FLUSH_COMPLETE: {
auto *event = static_cast<const struct chreAsyncResult *>(eventData);
mSawFlushCompleteEvent = event->success && event->cookie == &mCookie;
break;
}
case CHRE_EVENT_TEST_EVENT: {
auto event = static_cast<const TestEvent *>(eventData);
switch (event->type) {
case START_SCAN: {
const bool success = chreBleStartScanAsync(
CHRE_BLE_SCAN_MODE_AGGRESSIVE, 60000, nullptr);
TestEventQueueSingleton::get()->pushEvent(START_SCAN, success);
break;
}
case STOP_SCAN: {
const bool success = chreBleStopScanAsync();
TestEventQueueSingleton::get()->pushEvent(STOP_SCAN, success);
break;
}
case CALL_FLUSH: {
const bool success = chreBleFlushAsync(&mCookie);
TestEventQueueSingleton::get()->pushEvent(CALL_FLUSH, success);
break;
}
}
break;
}
}
if (mSawBleAdvertisementEvent && mSawFlushCompleteEvent) {
TestEventQueueSingleton::get()->pushEvent(
SAW_BLE_AD_AND_FLUSH_COMPLETE);
mSawBleAdvertisementEvent = false;
mSawFlushCompleteEvent = false;
}
}
private:
uint32_t mCookie;
bool mSawBleAdvertisementEvent = false;
bool mSawFlushCompleteEvent = false;
};
uint64_t appId = loadNanoapp(MakeUnique<App>());
// Flushing before a scan should fail.
bool success;
sendEventToNanoapp(appId, CALL_FLUSH);
waitForEvent(CALL_FLUSH, &success);
ASSERT_FALSE(success);
// Start a scan with batching.
sendEventToNanoapp(appId, START_SCAN);
waitForEvent(START_SCAN, &success);
ASSERT_TRUE(success);
waitForEvent(SCAN_STARTED);
ASSERT_TRUE(chrePalIsBleEnabled());
// Call flush again multiple times and get the complete event.
// We should only receive data when flush is called as the batch
// delay is extremely large.
constexpr uint32_t kNumFlushCalls = 3;
for (uint32_t i = 0; i < kNumFlushCalls; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
sendEventToNanoapp(appId, CALL_FLUSH);
waitForEvent(CALL_FLUSH, &success);
ASSERT_TRUE(success);
// Wait for some data and a flush complete.
// This ensures we receive both advertisement events
// and a flush complete event. We are not guaranteed
// that the advertisement events will come after
// the CALL_FLUSH event or before. If they come
// before, then they will be ignored. This
// change allows the advertisement events to come
// after during the normal expiration of the
// batch timer, which is valid (call flush, get
// any advertisement events, flush complete event
// might get some advertisement events afterwards).
waitForEvent(SAW_BLE_AD_AND_FLUSH_COMPLETE);
}
// Stop a scan.
sendEventToNanoapp(appId, STOP_SCAN);
waitForEvent(STOP_SCAN, &success);
ASSERT_TRUE(success);
waitForEvent(SCAN_STOPPED);
ASSERT_FALSE(chrePalIsBleEnabled());
// Flushing after a scan should fail.
sendEventToNanoapp(appId, CALL_FLUSH);
waitForEvent(CALL_FLUSH, &success);
ASSERT_FALSE(success);
}
} // namespace
} // namespace chre