You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1144 lines
39 KiB

/*
* Copyright (C) 2020 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 "transport_test.h"
#include <gtest/gtest.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <thread>
#include "chpp/app.h"
#include "chpp/common/discovery.h"
#include "chpp/common/gnss.h"
#include "chpp/common/gnss_types.h"
#include "chpp/common/standard_uuids.h"
#include "chpp/common/wifi.h"
#include "chpp/common/wifi_types.h"
#include "chpp/common/wwan.h"
#include "chpp/crc.h"
#include "chpp/macros.h"
#include "chpp/memory.h"
#include "chpp/platform/utils.h"
#include "chpp/services/discovery.h"
#include "chpp/services/loopback.h"
#include "chpp/transport.h"
#include "chre/pal/wwan.h"
namespace {
// Preamble as separate bytes for testing
constexpr uint8_t kChppPreamble0 = 0x68;
constexpr uint8_t kChppPreamble1 = 0x43;
// Max size of payload sent to chppRxDataCb (bytes)
constexpr size_t kMaxChunkSize = 20000;
constexpr size_t kMaxPacketSize = kMaxChunkSize + CHPP_PREAMBLE_LEN_BYTES +
sizeof(ChppTransportHeader) +
sizeof(ChppTransportFooter);
// Input sizes to test the entire range of sizes with a few tests
constexpr int kChunkSizes[] = {0, 1, 2, 3, 4, 21, 100, 1000, 10001, 20000};
// Number of services
constexpr int kServiceCount = 3;
/*
* Test suite for the CHPP Transport Layer
*/
class TransportTests : public testing::TestWithParam<int> {
protected:
void SetUp() override {
chppClearTotalAllocBytes();
memset(&mTransportContext.linkParams, 0,
sizeof(mTransportContext.linkParams));
mTransportContext.linkParams.linkEstablished = true;
chppTransportInit(&mTransportContext, &mAppContext);
chppAppInit(&mAppContext, &mTransportContext);
mTransportContext.resetState = CHPP_RESET_STATE_NONE;
// Make sure CHPP has a correct count of the number of registered services
// on this platform, (in this case, 1,) as registered in the function
// chppRegisterCommonServices().
ASSERT_EQ(mAppContext.registeredServiceCount, kServiceCount);
}
void TearDown() override {
chppAppDeinit(&mAppContext);
chppTransportDeinit(&mTransportContext);
EXPECT_EQ(chppGetTotalAllocBytes(), 0);
}
ChppTransportState mTransportContext = {};
ChppAppState mAppContext = {};
uint8_t mBuf[kMaxPacketSize] = {};
};
/**
* Wait for chppTransportDoWork() to finish after it is notified by
* chppEnqueueTxPacket to run.
*
* TODO: (b/177616847) Improve test robustness / synchronization without adding
* overhead to CHPP
*/
void WaitForTransport(struct ChppTransportState *transportContext) {
// Wait for linkParams.notifier.signal to be triggered and processed
volatile uint32_t k = 1;
while (transportContext->linkParams.notifier.signal == 0 && k > 0) {
k++;
}
while (transportContext->linkParams.notifier.signal != 0 && k > 0) {
k++;
}
ASSERT_FALSE(k == 0);
while (k < UINT16_MAX) {
k++;
}
while (k > 0) {
k--;
}
// Should have reset loc and length for next packet / datagram
EXPECT_EQ(transportContext->rxStatus.locInDatagram, 0);
EXPECT_EQ(transportContext->rxDatagram.length, 0);
}
/**
* Validates a ChppTestResponse. Since the error field within the
* ChppAppHeader struct is optional (and not used for common services), this
* function returns the error field to be checked if desired, depending on the
* service.
*
* @param buf Buffer containing response.
* @param ackSeq Ack sequence to be verified.
* @param handle Handle number to be verified
* @param transactionID Transaction ID to be verified.
*
* @return The error field within the ChppAppHeader struct that is used by some
* but not all services.
*/
uint8_t validateChppTestResponse(void *buf, uint8_t ackSeq, uint8_t handle,
uint8_t transactionID) {
struct ChppTestResponse *response = (ChppTestResponse *)buf;
// Check preamble
EXPECT_EQ(response->preamble0, kChppPreamble0);
EXPECT_EQ(response->preamble1, kChppPreamble1);
// Check response transport headers
EXPECT_EQ(response->transportHeader.packetCode, CHPP_TRANSPORT_ERROR_NONE);
EXPECT_EQ(response->transportHeader.ackSeq, ackSeq);
// Check response app headers
EXPECT_EQ(response->appHeader.handle, handle);
EXPECT_EQ(response->appHeader.type, CHPP_MESSAGE_TYPE_SERVICE_RESPONSE);
EXPECT_EQ(response->appHeader.transaction, transactionID);
// Return optional response error to be checked if desired
return response->appHeader.error;
}
/**
* Aborts a packet and validates state.
*
* @param transportcontext Maintains status for each transport layer instance.
*/
void endAndValidatePacket(struct ChppTransportState *transportContext) {
chppRxPacketCompleteCb(transportContext);
EXPECT_EQ(transportContext->rxStatus.state, CHPP_STATE_PREAMBLE);
EXPECT_EQ(transportContext->rxStatus.locInDatagram, 0);
EXPECT_EQ(transportContext->rxDatagram.length, 0);
}
/**
* Adds a preamble to a certain location in a buffer, and increases the location
* accordingly, to account for the length of the added preamble.
*
* @param buf Buffer.
* @param location Location to add the preamble, which its value will be
* increased accordingly.
*/
void addPreambleToBuf(uint8_t *buf, size_t *location) {
buf[(*location)++] = kChppPreamble0;
buf[(*location)++] = kChppPreamble1;
}
/**
* Adds a transport header (with default values) to a certain location in a
* buffer, and increases the location accordingly, to account for the length of
* the added transport header.
*
* @param buf Buffer.
* @param location Location to add the transport header, which its value will be
* increased accordingly.
*
* @return Pointer to the added transport header (e.g. to modify its fields).
*/
ChppTransportHeader *addTransportHeaderToBuf(uint8_t *buf, size_t *location) {
size_t oldLoc = *location;
// Default values for initial, minimum size request packet
ChppTransportHeader transHeader = {};
transHeader.flags = CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM;
transHeader.packetCode = CHPP_TRANSPORT_ERROR_NONE;
transHeader.ackSeq = 1;
transHeader.seq = 0;
transHeader.length = sizeof(ChppAppHeader);
transHeader.reserved = 0;
memcpy(&buf[*location], &transHeader, sizeof(transHeader));
*location += sizeof(transHeader);
return (ChppTransportHeader *)&buf[oldLoc];
}
/**
* Adds an app header (with default values) to a certain location in a buffer,
* and increases the location accordingly, to account for the length of the
* added app header.
*
* @param buf Buffer.
* @param location Location to add the app header, which its value will be
* increased accordingly.
*
* @return Pointer to the added app header (e.g. to modify its fields).
*/
ChppAppHeader *addAppHeaderToBuf(uint8_t *buf, size_t *location) {
size_t oldLoc = *location;
// Default values - to be updated later as necessary
ChppAppHeader appHeader = {};
appHeader.handle = CHPP_HANDLE_NEGOTIATED_RANGE_START;
appHeader.type = CHPP_MESSAGE_TYPE_CLIENT_REQUEST;
appHeader.transaction = 0;
appHeader.error = CHPP_APP_ERROR_NONE;
appHeader.command = 0;
memcpy(&buf[*location], &appHeader, sizeof(appHeader));
*location += sizeof(appHeader);
return (ChppAppHeader *)&buf[oldLoc];
}
/**
* Adds a transport footer to a certain location in a buffer, and increases the
* location accordingly, to account for the length of the added preamble.
*
* @param buf Buffer.
* @param location Location to add the footer. The value of location will be
* increased accordingly.
*
*/
void addTransportFooterToBuf(uint8_t *buf, size_t *location) {
uint32_t *checksum = (uint32_t *)&buf[*location];
*checksum = chppCrc32(0, &buf[CHPP_PREAMBLE_LEN_BYTES],
*location - CHPP_PREAMBLE_LEN_BYTES);
*location += sizeof(ChppTransportFooter);
}
/**
* Opens a service and checks to make sure it was opened correctly.
*
* @param transportContext Transport layer context.
* @param buf Buffer.
* @param ackSeq Ack sequence of the packet to be sent out
* @param seq Sequence number of the packet to be sent out.
* @param handle Handle of the service to be opened.
* @param transactionID Transaction ID for the open request.
* @param command Open command.
*/
void openService(ChppTransportState *transportContext, uint8_t *buf,
uint8_t ackSeq, uint8_t seq, uint8_t handle,
uint8_t transactionID, uint16_t command) {
size_t len = 0;
addPreambleToBuf(buf, &len);
ChppTransportHeader *transHeader = addTransportHeaderToBuf(buf, &len);
transHeader->ackSeq = ackSeq;
transHeader->seq = seq;
ChppAppHeader *appHeader = addAppHeaderToBuf(buf, &len);
appHeader->handle = handle;
appHeader->transaction = transactionID;
appHeader->command = command;
addTransportFooterToBuf(buf, &len);
// Send header + payload (if any) + footer
EXPECT_TRUE(chppRxDataCb(transportContext, buf, len));
// Check for correct state
uint8_t nextSeq = transHeader->seq + 1;
EXPECT_EQ(transportContext->rxStatus.expectedSeq, nextSeq);
EXPECT_EQ(transportContext->rxStatus.state, CHPP_STATE_PREAMBLE);
// Wait for response
WaitForTransport(transportContext);
// Validate common response fields
EXPECT_EQ(validateChppTestResponse(transportContext->pendingTxPacket.payload,
nextSeq, handle, transactionID),
CHPP_APP_ERROR_NONE);
// Check response length
EXPECT_EQ(sizeof(ChppTestResponse), CHPP_PREAMBLE_LEN_BYTES +
sizeof(ChppTransportHeader) +
sizeof(ChppAppHeader));
EXPECT_EQ(transportContext->pendingTxPacket.length,
sizeof(ChppTestResponse) + sizeof(ChppTransportFooter));
}
/**
* Sends a command to a service and checks for errors.
*
* @param transportContext Transport layer context.
* @param buf Buffer.
* @param ackSeq Ack sequence of the packet to be sent out
* @param seq Sequence number of the packet to be sent out.
* @param handle Handle of the service to be opened.
* @param transactionID Transaction ID for the open request.
* @param command Command to be sent.
*/
void sendCommandToService(ChppTransportState *transportContext, uint8_t *buf,
uint8_t ackSeq, uint8_t seq, uint8_t handle,
uint8_t transactionID, uint16_t command) {
size_t len = 0;
addPreambleToBuf(buf, &len);
ChppTransportHeader *transHeader = addTransportHeaderToBuf(buf, &len);
transHeader->ackSeq = ackSeq;
transHeader->seq = seq;
ChppAppHeader *appHeader = addAppHeaderToBuf(buf, &len);
appHeader->handle = handle;
appHeader->transaction = transactionID;
appHeader->command = command;
addTransportFooterToBuf(buf, &len);
// Send header + payload (if any) + footer
EXPECT_TRUE(chppRxDataCb(transportContext, buf, len));
// Check for correct state
uint8_t nextSeq = transHeader->seq + 1;
EXPECT_EQ(transportContext->rxStatus.expectedSeq, nextSeq);
EXPECT_EQ(transportContext->rxStatus.state, CHPP_STATE_PREAMBLE);
// Wait for response
WaitForTransport(transportContext);
// Validate common response fields
EXPECT_EQ(validateChppTestResponse(transportContext->pendingTxPacket.payload,
nextSeq, handle, transactionID),
CHPP_APP_ERROR_NONE);
}
/**
* A series of zeros shouldn't change state from CHPP_STATE_PREAMBLE
*/
TEST_P(TransportTests, ZeroNoPreambleInput) {
size_t len = static_cast<size_t>(GetParam());
if (len <= kMaxChunkSize) {
EXPECT_TRUE(chppRxDataCb(&mTransportContext, mBuf, len));
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_PREAMBLE);
}
}
/**
* A preamble after a series of zeros input should change state from
* CHPP_STATE_PREAMBLE to CHPP_STATE_HEADER
*/
TEST_P(TransportTests, ZeroThenPreambleInput) {
size_t len = static_cast<size_t>(GetParam());
if (len <= kMaxChunkSize) {
// Add preamble at the end of mBuf, as individual bytes instead of using
// chppAddPreamble(&mBuf[preambleLoc])
size_t preambleLoc = MAX(0, len - CHPP_PREAMBLE_LEN_BYTES);
mBuf[preambleLoc] = kChppPreamble0;
mBuf[preambleLoc + 1] = kChppPreamble1;
if (len >= CHPP_PREAMBLE_LEN_BYTES) {
EXPECT_FALSE(chppRxDataCb(&mTransportContext, mBuf, len));
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_HEADER);
} else {
EXPECT_TRUE(chppRxDataCb(&mTransportContext, mBuf, len));
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_PREAMBLE);
}
}
}
/**
* Rx Testing with various length payloads of zeros
*/
TEST_P(TransportTests, RxPayloadOfZeros) {
mTransportContext.rxStatus.state = CHPP_STATE_PREAMBLE;
size_t len = static_cast<size_t>(GetParam());
bool validLen = (len <= CHPP_TRANSPORT_RX_MTU_BYTES);
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
if (len <= kMaxChunkSize) {
size_t loc = 0;
addPreambleToBuf(mBuf, &loc);
ChppTransportHeader *transHeader = addTransportHeaderToBuf(mBuf, &loc);
transHeader->length = static_cast<uint16_t>(len);
loc += len;
addTransportFooterToBuf(mBuf, &loc);
// Send header and check for correct state
EXPECT_EQ(
chppRxDataCb(&mTransportContext, mBuf,
CHPP_PREAMBLE_LEN_BYTES + sizeof(ChppTransportHeader)),
!validLen);
if (!validLen) {
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_PREAMBLE);
} else if (len > 0) {
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_PAYLOAD);
} else {
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_FOOTER);
}
// Correct decoding of packet length
EXPECT_EQ(mTransportContext.rxHeader.length, len);
EXPECT_EQ(mTransportContext.rxStatus.locInDatagram, 0);
EXPECT_EQ(mTransportContext.rxDatagram.length, validLen ? len : 0);
// Send payload if any and check for correct state
if (len > 0) {
EXPECT_EQ(
chppRxDataCb(
&mTransportContext,
&mBuf[CHPP_PREAMBLE_LEN_BYTES + sizeof(ChppTransportHeader)],
len),
!validLen);
EXPECT_EQ(mTransportContext.rxStatus.state,
validLen ? CHPP_STATE_FOOTER : CHPP_STATE_PREAMBLE);
}
// Should have complete packet payload by now
EXPECT_EQ(mTransportContext.rxStatus.locInDatagram, validLen ? len : 0);
// But no ACK yet
EXPECT_EQ(mTransportContext.rxStatus.expectedSeq, transHeader->seq);
// Send footer
EXPECT_TRUE(chppRxDataCb(
&mTransportContext,
&mBuf[CHPP_PREAMBLE_LEN_BYTES + sizeof(ChppTransportHeader) + len],
sizeof(ChppTransportFooter)));
// The next expected packet sequence # should incremented only if the
// received packet is payload-bearing.
uint8_t nextSeq = transHeader->seq + ((validLen && len > 0) ? 1 : 0);
EXPECT_EQ(mTransportContext.rxStatus.expectedSeq, nextSeq);
// Check for correct ACK crafting if applicable (i.e. if the received packet
// is payload-bearing).
if (validLen && len > 0) {
// TODO: Remove later as can cause flaky tests
// These are expected to change shortly afterwards, as chppTransportDoWork
// is run
// EXPECT_TRUE(mTransportContext.txStatus.hasPacketsToSend);
EXPECT_EQ(mTransportContext.txStatus.packetCodeToSend,
CHPP_TRANSPORT_ERROR_NONE);
EXPECT_EQ(mTransportContext.txDatagramQueue.pending, 0);
WaitForTransport(&mTransportContext);
// Check response packet fields
struct ChppTransportHeader *txHeader =
(struct ChppTransportHeader *)&mTransportContext.pendingTxPacket
.payload[CHPP_PREAMBLE_LEN_BYTES];
EXPECT_EQ(txHeader->flags, CHPP_TRANSPORT_FLAG_FINISHED_DATAGRAM);
EXPECT_EQ(txHeader->packetCode, CHPP_TRANSPORT_ERROR_NONE);
EXPECT_EQ(txHeader->ackSeq, nextSeq);
EXPECT_EQ(txHeader->length, 0);
// Check outgoing packet length
EXPECT_EQ(mTransportContext.pendingTxPacket.length,
CHPP_PREAMBLE_LEN_BYTES + sizeof(struct ChppTransportHeader) +
sizeof(struct ChppTransportFooter));
}
// Check for correct state
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_PREAMBLE);
// Should have reset loc and length for next packet / datagram
EXPECT_EQ(mTransportContext.rxStatus.locInDatagram, 0);
EXPECT_EQ(mTransportContext.rxDatagram.length, 0);
}
chppWorkThreadStop(&mTransportContext);
t1.join();
}
/**
* End of Packet Link Notification during preamble
*/
TEST_F(TransportTests, LinkSendDonePreamble) {
size_t payloadLen = 1000;
size_t partLenPreamble = CHPP_PREAMBLE_LEN_BYTES - 1;
mTransportContext.rxStatus.state = CHPP_STATE_PREAMBLE;
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
size_t loc = 0;
addPreambleToBuf(mBuf, &loc);
ChppTransportHeader *transHeader = addTransportHeaderToBuf(mBuf, &loc);
transHeader->length = static_cast<uint16_t>(payloadLen);
loc += payloadLen;
addTransportFooterToBuf(mBuf, &loc);
EXPECT_FALSE(chppRxDataCb(&mTransportContext, mBuf, partLenPreamble));
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_PREAMBLE);
endAndValidatePacket(&mTransportContext);
chppWorkThreadStop(&mTransportContext);
t1.join();
}
/**
* End of Packet Link Notification during header
*/
TEST_F(TransportTests, LinkSendDoneHeader) {
size_t payloadLen = 1000;
size_t partLenHeader =
CHPP_PREAMBLE_LEN_BYTES + sizeof(ChppTransportHeader) - 1;
mTransportContext.rxStatus.state = CHPP_STATE_PREAMBLE;
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
size_t loc = 0;
addPreambleToBuf(mBuf, &loc);
ChppTransportHeader *transHeader = addTransportHeaderToBuf(mBuf, &loc);
transHeader->length = static_cast<uint16_t>(payloadLen);
loc += payloadLen;
addTransportFooterToBuf(mBuf, &loc);
EXPECT_FALSE(chppRxDataCb(&mTransportContext, mBuf, partLenHeader));
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_HEADER);
EXPECT_EQ(mTransportContext.rxHeader.length, payloadLen);
endAndValidatePacket(&mTransportContext);
chppWorkThreadStop(&mTransportContext);
t1.join();
}
/**
* End of Packet Link Notification during payload
*/
TEST_F(TransportTests, LinkSendDonePayload) {
size_t payloadLen = 1000;
size_t partLenPayload = 500;
mTransportContext.rxStatus.state = CHPP_STATE_PREAMBLE;
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
size_t loc = 0;
addPreambleToBuf(mBuf, &loc);
ChppTransportHeader *transHeader = addTransportHeaderToBuf(mBuf, &loc);
transHeader->length = static_cast<uint16_t>(payloadLen);
loc += payloadLen;
addTransportFooterToBuf(mBuf, &loc);
EXPECT_FALSE(chppRxDataCb(&mTransportContext, mBuf, partLenPayload));
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_PAYLOAD);
EXPECT_EQ(mTransportContext.rxHeader.length, payloadLen);
EXPECT_EQ(
mTransportContext.rxStatus.locInDatagram,
partLenPayload - CHPP_PREAMBLE_LEN_BYTES - sizeof(ChppTransportHeader));
EXPECT_EQ(mTransportContext.rxDatagram.length, payloadLen);
endAndValidatePacket(&mTransportContext);
chppWorkThreadStop(&mTransportContext);
t1.join();
}
/**
* End of Packet Link Notification during footer
*/
TEST_F(TransportTests, LinkSendDoneFooter) {
size_t payloadLen = 1000;
size_t partLenFooter = CHPP_PREAMBLE_LEN_BYTES + sizeof(ChppTransportHeader) +
payloadLen + sizeof(ChppTransportFooter) - 1;
mTransportContext.rxStatus.state = CHPP_STATE_PREAMBLE;
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
size_t loc = 0;
addPreambleToBuf(mBuf, &loc);
ChppTransportHeader *transHeader = addTransportHeaderToBuf(mBuf, &loc);
transHeader->length = static_cast<uint16_t>(payloadLen);
loc += payloadLen;
addTransportFooterToBuf(mBuf, &loc);
EXPECT_FALSE(chppRxDataCb(&mTransportContext, mBuf, partLenFooter));
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_FOOTER);
EXPECT_EQ(mTransportContext.rxHeader.length, payloadLen);
EXPECT_EQ(mTransportContext.rxStatus.locInDatagram, payloadLen);
EXPECT_EQ(mTransportContext.rxDatagram.length, payloadLen);
endAndValidatePacket(&mTransportContext);
chppWorkThreadStop(&mTransportContext);
t1.join();
}
TEST_P(TransportTests, EnqueueDatagrams) {
size_t len = static_cast<size_t>(GetParam());
if (len <= CHPP_TX_DATAGRAM_QUEUE_LEN) {
// Add (len) datagrams of various length to queue
int fr = 0;
for (int j = 0; j == CHPP_TX_DATAGRAM_QUEUE_LEN; j++) {
for (size_t i = 1; i <= len; i++) {
uint8_t *mBuf = (uint8_t *)chppMalloc(i + 100);
EXPECT_TRUE(
chppEnqueueTxDatagramOrFail(&mTransportContext, mBuf, i + 100));
EXPECT_EQ(mTransportContext.txDatagramQueue.pending, i);
EXPECT_EQ(mTransportContext.txDatagramQueue.front, fr);
EXPECT_EQ(mTransportContext.txDatagramQueue
.datagram[(i - 1 + fr) % CHPP_TX_DATAGRAM_QUEUE_LEN]
.length,
i + 100);
}
if (mTransportContext.txDatagramQueue.pending ==
CHPP_TX_DATAGRAM_QUEUE_LEN) {
uint8_t *mBuf = (uint8_t *)chppMalloc(100);
EXPECT_FALSE(
chppEnqueueTxDatagramOrFail(&mTransportContext, mBuf, 100));
CHPP_FREE_AND_NULLIFY(mBuf);
}
for (size_t i = len; i > 0; i--) {
fr++;
fr %= CHPP_TX_DATAGRAM_QUEUE_LEN;
EXPECT_TRUE(chppDequeueTxDatagram(&mTransportContext));
EXPECT_EQ(mTransportContext.txDatagramQueue.front, fr);
EXPECT_EQ(mTransportContext.txDatagramQueue.pending, i - 1);
}
EXPECT_FALSE(chppDequeueTxDatagram(&mTransportContext));
EXPECT_EQ(mTransportContext.txDatagramQueue.front, fr);
EXPECT_EQ(mTransportContext.txDatagramQueue.pending, 0);
}
}
}
/**
* Loopback testing with various length payloads of zeros
*/
TEST_P(TransportTests, LoopbackPayloadOfZeros) {
mTransportContext.rxStatus.state = CHPP_STATE_PREAMBLE;
size_t len = static_cast<size_t>(GetParam());
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
chppWorkThreadStop(&mTransportContext);
t1.join();
if (len > 1 && len <= kMaxChunkSize) {
// Loopback App header (only 2 fields required)
mBuf[0] = CHPP_HANDLE_LOOPBACK;
mBuf[1] = CHPP_MESSAGE_TYPE_CLIENT_REQUEST;
EXPECT_TRUE(chppDispatchLoopbackClientRequest(&mAppContext, mBuf, len));
uint16_t end = (mTransportContext.txDatagramQueue.front +
mTransportContext.txDatagramQueue.pending - 1) %
CHPP_TX_DATAGRAM_QUEUE_LEN;
EXPECT_EQ(mTransportContext.txDatagramQueue.datagram[end].length, len);
EXPECT_EQ(mTransportContext.txDatagramQueue.datagram[end].payload[0],
CHPP_HANDLE_LOOPBACK);
EXPECT_EQ(mTransportContext.txDatagramQueue.datagram[end].payload[1],
CHPP_MESSAGE_TYPE_SERVICE_RESPONSE);
}
}
/**
* Discovery service + Transaction ID
*/
TEST_P(TransportTests, DiscoveryAndTransactionID) {
uint8_t transactionID = static_cast<uint8_t>(GetParam());
size_t len = 0;
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
chppWorkThreadStop(&mTransportContext);
t1.join();
ChppAppHeader *appHeader = addAppHeaderToBuf(mBuf, &len);
appHeader->handle = CHPP_HANDLE_DISCOVERY;
appHeader->transaction = transactionID;
appHeader->command = CHPP_DISCOVERY_COMMAND_DISCOVER_ALL;
EXPECT_TRUE(chppDispatchDiscoveryClientRequest(&mAppContext, mBuf, len));
uint16_t end = (mTransportContext.txDatagramQueue.front +
mTransportContext.txDatagramQueue.pending - 1) %
CHPP_TX_DATAGRAM_QUEUE_LEN;
struct ChppAppHeader *responseHeader =
(ChppAppHeader *)mTransportContext.txDatagramQueue.datagram[end].payload;
EXPECT_EQ(responseHeader->handle, CHPP_HANDLE_DISCOVERY);
EXPECT_EQ(responseHeader->type, CHPP_MESSAGE_TYPE_SERVICE_RESPONSE);
EXPECT_EQ(responseHeader->transaction, transactionID);
EXPECT_EQ(responseHeader->error, CHPP_APP_ERROR_NONE);
EXPECT_EQ(responseHeader->command, CHPP_DISCOVERY_COMMAND_DISCOVER_ALL);
// Decode discovery response
ChppServiceDescriptor *services =
(ChppServiceDescriptor *)&mTransportContext.txDatagramQueue.datagram[end]
.payload[sizeof(ChppAppHeader)];
// Check total length (and implicit service count)
EXPECT_EQ(
mTransportContext.txDatagramQueue.datagram[end].length,
sizeof(ChppAppHeader) + kServiceCount * sizeof(ChppServiceDescriptor));
// Check service configuration response
ChppServiceDescriptor wwanServiceDescriptor = {};
static const uint8_t uuid[CHPP_SERVICE_UUID_LEN] = CHPP_UUID_WWAN_STANDARD;
memcpy(&wwanServiceDescriptor.uuid, &uuid,
sizeof(wwanServiceDescriptor.uuid));
static const char name[CHPP_SERVICE_NAME_MAX_LEN] = "WWAN";
memcpy(&wwanServiceDescriptor.name, &name,
sizeof(wwanServiceDescriptor.name));
wwanServiceDescriptor.version.major = 1;
wwanServiceDescriptor.version.minor = 0;
wwanServiceDescriptor.version.patch = 0;
EXPECT_EQ(std::memcmp(services[0].uuid, wwanServiceDescriptor.uuid,
sizeof(wwanServiceDescriptor.uuid)),
0);
EXPECT_EQ(std::memcmp(services[0].name, wwanServiceDescriptor.name,
sizeof(wwanServiceDescriptor.name)),
0);
EXPECT_EQ(services[0].version.major, wwanServiceDescriptor.version.major);
EXPECT_EQ(services[0].version.minor, wwanServiceDescriptor.version.minor);
EXPECT_EQ(services[0].version.patch, wwanServiceDescriptor.version.patch);
}
/**
* CRC-32 calculation for several pre-known test vectors.
*/
TEST_F(TransportTests, CRC32Basic) {
static const char kTest1Str[] = "Hello World Test!";
static const uint8_t *kTest1 = (const uint8_t *)kTest1Str;
EXPECT_EQ(chppCrc32(0, kTest1, 17), 0x613B1D74);
EXPECT_EQ(chppCrc32(0, kTest1, 16), 0x5F88D7D9);
EXPECT_EQ(chppCrc32(0, kTest1, 1), 0xAA05262F);
EXPECT_EQ(chppCrc32(0, kTest1, 0), 0x00000000);
static const uint8_t kTest2[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
EXPECT_EQ(chppCrc32(0, kTest2, 6), 0x41D9ED00);
EXPECT_EQ(chppCrc32(0, kTest2, 5), 0xD2FD1072);
EXPECT_EQ(chppCrc32(0, kTest2, 4), 0xFFFFFFFF);
EXPECT_EQ(chppCrc32(0, kTest2, 3), 0xFFFFFF00);
EXPECT_EQ(chppCrc32(0, kTest2, 2), 0xFFFF0000);
EXPECT_EQ(chppCrc32(0, kTest2, 1), 0xFF000000);
EXPECT_EQ(chppCrc32(0, kTest2, 0), 0x00000000);
static const char kTest3Str[] = "123456789";
static const uint8_t *kTest3 = (const uint8_t *)kTest3Str;
EXPECT_EQ(chppCrc32(0, kTest3, 9), 0xCBF43926);
static const uint8_t kTest4[] = {0x00, 0x00, 0x00, 0x00};
EXPECT_EQ(chppCrc32(0, kTest4, sizeof(kTest4)), 0x2144DF1C);
static const uint8_t kTest5[] = {0xF2, 0x01, 0x83};
EXPECT_EQ(chppCrc32(0, kTest5, sizeof(kTest5)), 0x24AB9D77);
static const uint8_t kTest6[] = {0x0F, 0xAA, 0x00, 0x55};
EXPECT_EQ(chppCrc32(0, kTest6, sizeof(kTest6)), 0xB6C9B287);
static const uint8_t kTest7[] = {0x00, 0xFF, 0x55, 0x11};
EXPECT_EQ(chppCrc32(0, kTest7, sizeof(kTest7)), 0x32A06212);
static const uint8_t kTest8[] = {0x33, 0x22, 0x55, 0xAA, 0xBB,
0xCC, 0xDD, 0xEE, 0xFF};
EXPECT_EQ(chppCrc32(0, kTest8, sizeof(kTest8)), 0xB0AE863D);
static const uint8_t kTest9[] = {0x92, 0x6B, 0x55};
EXPECT_EQ(chppCrc32(0, kTest9, sizeof(kTest9)), 0x9CDEA29B);
}
/**
* CRC-32 calculation for daisy-chained input.
*/
TEST_F(TransportTests, CRC32DaisyChained) {
static const size_t kMaxLen = 10000;
uint8_t test[kMaxLen];
// Populate test with 8-bit LFSR
// Feedback polynomial is x^8 + x^6 + x^5 + x^4 + 1
static uint8_t lfsr = 1;
for (size_t i = 0; i < kMaxLen; i++) {
test[i] = lfsr;
lfsr = (lfsr >> 1) |
(((lfsr << 7) ^ (lfsr << 5) ^ (lfsr << 4) ^ (lfsr << 3)) & 0x80);
}
for (size_t len = 0; len < kMaxLen; len += 1000) {
uint32_t fullCRC = chppCrc32(0, &test[0], len);
for (size_t partition = 0; partition <= len; partition++) {
uint32_t partialCRC = chppCrc32(0, &test[0], partition);
EXPECT_EQ(chppCrc32(partialCRC, &test[partition], len - partition),
fullCRC);
}
}
}
/**
* WWAN service Open and GetCapabilities.
*/
TEST_F(TransportTests, WwanOpen) {
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
uint8_t ackSeq = 1;
uint8_t seq = 0;
uint8_t handle = CHPP_HANDLE_NEGOTIATED_RANGE_START;
uint8_t transactionID = 0;
size_t len = 0;
openService(&mTransportContext, mBuf, ackSeq++, seq++, handle,
transactionID++, CHPP_WWAN_OPEN);
addPreambleToBuf(mBuf, &len);
uint16_t command = CHPP_WWAN_GET_CAPABILITIES;
sendCommandToService(&mTransportContext, mBuf, ackSeq++, seq++, handle,
transactionID++, command);
size_t responseLoc = sizeof(ChppTestResponse);
// Validate capabilities
uint32_t *capabilities =
(uint32_t *)&mTransportContext.pendingTxPacket.payload[responseLoc];
responseLoc += sizeof(uint32_t);
// Cleanup
chppWorkThreadStop(&mTransportContext);
t1.join();
uint32_t capabilitySet = CHRE_WWAN_GET_CELL_INFO;
EXPECT_EQ((*capabilities) & ~(capabilitySet), 0);
// Check total length
EXPECT_EQ(responseLoc, CHPP_PREAMBLE_LEN_BYTES + sizeof(ChppTransportHeader) +
sizeof(ChppWwanGetCapabilitiesResponse));
}
/**
* WiFi service Open and GetCapabilities.
*/
TEST_F(TransportTests, WifiOpen) {
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
uint8_t ackSeq = 1;
uint8_t seq = 0;
uint8_t handle = CHPP_HANDLE_NEGOTIATED_RANGE_START + 1;
uint8_t transactionID = 0;
openService(&mTransportContext, mBuf, ackSeq++, seq++, handle,
transactionID++, CHPP_WIFI_OPEN);
uint16_t command = CHPP_WIFI_GET_CAPABILITIES;
sendCommandToService(&mTransportContext, mBuf, ackSeq++, seq++, handle,
transactionID++, command);
size_t responseLoc = sizeof(ChppTestResponse);
// Cleanup
chppWorkThreadStop(&mTransportContext);
t1.join();
// Validate capabilities
uint32_t *capabilities =
(uint32_t *)&mTransportContext.pendingTxPacket.payload[responseLoc];
responseLoc += sizeof(uint32_t);
uint32_t capabilitySet = CHRE_WIFI_CAPABILITIES_SCAN_MONITORING |
CHRE_WIFI_CAPABILITIES_ON_DEMAND_SCAN |
CHRE_WIFI_CAPABILITIES_RADIO_CHAIN_PREF |
CHRE_WIFI_CAPABILITIES_RTT_RANGING;
EXPECT_EQ((*capabilities) & ~(capabilitySet), 0);
// Check total length
EXPECT_EQ(responseLoc, CHPP_PREAMBLE_LEN_BYTES + sizeof(ChppTransportHeader) +
sizeof(ChppWwanGetCapabilitiesResponse));
}
/**
* GNSS service Open and GetCapabilities.
*/
TEST_F(TransportTests, GnssOpen) {
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
uint8_t ackSeq = 1;
uint8_t seq = 0;
uint8_t handle = CHPP_HANDLE_NEGOTIATED_RANGE_START + 2;
uint8_t transactionID = 0;
size_t len = 0;
openService(&mTransportContext, mBuf, ackSeq++, seq++, handle,
transactionID++, CHPP_GNSS_OPEN);
addPreambleToBuf(mBuf, &len);
uint16_t command = CHPP_GNSS_GET_CAPABILITIES;
sendCommandToService(&mTransportContext, mBuf, ackSeq++, seq++, handle,
transactionID++, command);
size_t responseLoc = sizeof(ChppTestResponse);
// Cleanup
chppWorkThreadStop(&mTransportContext);
t1.join();
// Validate capabilities
uint32_t *capabilities =
(uint32_t *)&mTransportContext.pendingTxPacket.payload[responseLoc];
responseLoc += sizeof(uint32_t);
uint32_t capabilitySet =
CHRE_GNSS_CAPABILITIES_LOCATION | CHRE_GNSS_CAPABILITIES_MEASUREMENTS |
CHRE_GNSS_CAPABILITIES_GNSS_ENGINE_BASED_PASSIVE_LISTENER;
EXPECT_EQ((*capabilities) & ~(capabilitySet), 0);
// Check total length
EXPECT_EQ(responseLoc, CHPP_PREAMBLE_LEN_BYTES + sizeof(ChppTransportHeader) +
sizeof(ChppGnssGetCapabilitiesResponse));
}
/**
* Discovery client.
*/
TEST_F(TransportTests, Discovery) {
size_t len = 0;
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
addPreambleToBuf(mBuf, &len);
ChppTransportHeader *transHeader = addTransportHeaderToBuf(mBuf, &len);
ChppAppHeader *appHeader = addAppHeaderToBuf(mBuf, &len);
appHeader->handle = CHPP_HANDLE_DISCOVERY;
appHeader->command = CHPP_DISCOVERY_COMMAND_DISCOVER_ALL;
appHeader->type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE;
ChppServiceDescriptor wwanServiceDescriptor = {};
static const uint8_t uuid[CHPP_SERVICE_UUID_LEN] = CHPP_UUID_WWAN_STANDARD;
memcpy(&wwanServiceDescriptor.uuid, &uuid,
sizeof(wwanServiceDescriptor.uuid));
static const char name[CHPP_SERVICE_NAME_MAX_LEN] = "WWAN";
memcpy(&wwanServiceDescriptor.name, &name,
sizeof(wwanServiceDescriptor.name));
wwanServiceDescriptor.version.major = 1;
wwanServiceDescriptor.version.minor = 0;
wwanServiceDescriptor.version.patch = 0;
memcpy(&mBuf[len], &wwanServiceDescriptor, sizeof(ChppServiceDescriptor));
len += sizeof(ChppServiceDescriptor);
transHeader->length = static_cast<uint16_t>(
len - sizeof(ChppTransportHeader) - CHPP_PREAMBLE_LEN_BYTES);
addTransportFooterToBuf(mBuf, &len);
// Initialize clientIndexOfServiceIndex[0] to see if it correctly updated
// upon discovery
mAppContext.clientIndexOfServiceIndex[0] = CHPP_CLIENT_INDEX_NONE;
// Send header + payload (if any) + footer
EXPECT_TRUE(chppRxDataCb(&mTransportContext, mBuf, len));
// Cleanup
chppWorkThreadStop(&mTransportContext);
t1.join();
// Check for correct state
EXPECT_EQ(mAppContext.clientIndexOfServiceIndex[0], 0);
uint8_t nextSeq = transHeader->seq + 1;
EXPECT_EQ(mTransportContext.rxStatus.expectedSeq, nextSeq);
EXPECT_EQ(mTransportContext.rxStatus.state, CHPP_STATE_PREAMBLE);
}
/**
* Unopened clients should not crash upon an unsolicitated service response.
*/
TEST_F(TransportTests, UnopenedClient) {
size_t len = 0;
uint8_t *buf = (uint8_t *)chppMalloc(100);
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
chppWorkThreadStop(&mTransportContext);
t1.join();
ChppAppHeader *appHeader = addAppHeaderToBuf(buf, &len);
appHeader->handle = CHPP_HANDLE_NEGOTIATED_RANGE_START + 1;
appHeader->command = CHPP_WIFI_CONFIGURE_SCAN_MONITOR_ASYNC;
appHeader->type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE;
len = sizeof(struct ChppWifiConfigureScanMonitorAsyncResponse);
ASSERT_EQ(mAppContext.registeredServiceCount, kServiceCount);
chppAppProcessRxDatagram(&mAppContext, buf, len);
EXPECT_EQ(mTransportContext.txStatus.packetCodeToSend,
CHPP_TRANSPORT_ERROR_APPLAYER);
}
TEST_F(TransportTests, DiscardedPacketTest) {
mTransportContext.txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, &mTransportContext);
WaitForTransport(&mTransportContext);
// Send packet to RX thread after manually setting to resetting state.
// We expect this packet to get dropped, but this test checks for any
// problematic behavior (e.g. memory leaks).
mTransportContext.resetState = CHPP_RESET_STATE_RESETTING;
size_t loc = 0;
addPreambleToBuf(mBuf, &loc);
ChppTransportHeader *transHeader = addTransportHeaderToBuf(mBuf, &loc);
ChppAppHeader *appHeader = addAppHeaderToBuf(mBuf, &loc);
appHeader->handle = CHPP_HANDLE_DISCOVERY;
appHeader->command = CHPP_DISCOVERY_COMMAND_DISCOVER_ALL;
appHeader->type = CHPP_MESSAGE_TYPE_SERVICE_RESPONSE;
ChppServiceDescriptor wwanServiceDescriptor = {};
static const uint8_t uuid[CHPP_SERVICE_UUID_LEN] = CHPP_UUID_WWAN_STANDARD;
memcpy(&wwanServiceDescriptor.uuid, &uuid,
sizeof(wwanServiceDescriptor.uuid));
static const char name[CHPP_SERVICE_NAME_MAX_LEN] = "WWAN";
memcpy(&wwanServiceDescriptor.name, &name,
sizeof(wwanServiceDescriptor.name));
wwanServiceDescriptor.version.major = 1;
wwanServiceDescriptor.version.minor = 0;
wwanServiceDescriptor.version.patch = 0;
memcpy(&mBuf[loc], &wwanServiceDescriptor, sizeof(ChppServiceDescriptor));
loc += sizeof(ChppServiceDescriptor);
transHeader->length = static_cast<uint16_t>(
loc - sizeof(ChppTransportHeader) - CHPP_PREAMBLE_LEN_BYTES);
addTransportFooterToBuf(mBuf, &loc);
mAppContext.clientIndexOfServiceIndex[0] = CHPP_CLIENT_INDEX_NONE;
EXPECT_TRUE(chppRxDataCb(&mTransportContext, mBuf, loc));
chppWorkThreadStop(&mTransportContext);
t1.join();
}
/*
* Correctly handle messages directed to clients / services with an invalid
* handle number.
*/
void messageToInvalidHandle(ChppTransportState *transportContext,
uint8_t type) {
size_t len = 0;
uint8_t *buf = (uint8_t *)chppMalloc(100);
transportContext->txStatus.hasPacketsToSend = true;
std::thread t1(chppWorkThreadStart, transportContext);
WaitForTransport(transportContext);
chppWorkThreadStop(transportContext);
t1.join();
ChppAppHeader *appHeader = addAppHeaderToBuf(buf, &len);
appHeader->handle =
CHPP_HANDLE_NEGOTIATED_RANGE_START + CHPP_MAX_REGISTERED_CLIENTS;
appHeader->type = type;
len = sizeof(struct ChppAppHeader);
chppAppProcessRxDatagram(transportContext->appContext, buf, len);
EXPECT_EQ(transportContext->txStatus.packetCodeToSend,
CHPP_TRANSPORT_ERROR_APPLAYER);
}
TEST_F(TransportTests, RequestToInvalidService) {
messageToInvalidHandle(&mTransportContext, CHPP_MESSAGE_TYPE_CLIENT_REQUEST);
}
TEST_F(TransportTests, ResponseToInvalidClient) {
messageToInvalidHandle(&mTransportContext,
CHPP_MESSAGE_TYPE_SERVICE_RESPONSE);
}
TEST_F(TransportTests, NotificationToInvalidService) {
messageToInvalidHandle(&mTransportContext,
CHPP_MESSAGE_TYPE_CLIENT_NOTIFICATION);
}
TEST_F(TransportTests, NotificationToInvalidClient) {
messageToInvalidHandle(&mTransportContext,
CHPP_MESSAGE_TYPE_SERVICE_NOTIFICATION);
}
INSTANTIATE_TEST_SUITE_P(TransportTestRange, TransportTests,
testing::ValuesIn(kChunkSizes));
} // namespace