blob: 12cad117a70f55147121c33d228b5fc776dfa758 [file] [log] [blame]
/* Copyright (C) 2016 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
#include "android/telephony/modem.h"
#include "android/base/ArraySize.h"
#include <gtest/gtest.h>
#include <stdio.h>
#include <string.h>
static const int kBasePort = 42;
class ModemTest : public ::testing::Test {
protected:
void SetUp() override {
mModem = amodem_create(kBasePort,
&ModemTest::onUnsolicitedCommand,
this);
}
void TearDown() override {
if (mModem) {
amodem_destroy(mModem);
mModem = nullptr;
}
}
static void onUnsolicitedCommand(void* opaque, const char* message) {
}
int openLogicalChannel(const char* name) {
// Create and send command
char command[1024];
snprintf(command, sizeof(command), "AT+CCHO=%s", name);
command[sizeof(command) - 1] = '\0';
const char* reply = amodem_send(mModem, command);
if (reply == nullptr) {
return -1;
}
// Parse the reply
int channel;
char ok[4] = { 0 };
char dummy;
if (sscanf(reply, "%d%3c%c", &channel, ok, &dummy) != 2) {
return -2;
}
if (strcmp(ok, "\rOK") != 0) {
return -3;
}
return channel;
}
bool closeLogicalChannel(int channel) {
char command[128];
snprintf(command, sizeof(command), "AT+CCHC=%d", channel);
command[sizeof(command) - 1] = '\0';
const char* reply = amodem_send(mModem, command);
return strcmp("+CCHC\rOK", reply) == 0;
}
AModem mModem;
};
TEST_F(ModemTest, OpenLogicalChannel) {
// Open a lgoical channel (CCHO operation) with some random name
int firstChannel = openLogicalChannel("F6238748383");
// Channels must start at 1 or above
ASSERT_GE(firstChannel, 1);
int secondChannel = openLogicalChannel("F6238748383");
ASSERT_GE(secondChannel, 1);
// Make sure we got a different channel
ASSERT_NE(firstChannel, secondChannel);
closeLogicalChannel(firstChannel);
closeLogicalChannel(secondChannel);
}
TEST_F(ModemTest, CloseLogicalChannel) {
int channel = openLogicalChannel("F6238748383");
ASSERT_GE(channel, 1);
// Should not be able to close another channel
ASSERT_FALSE(closeLogicalChannel(channel + 1));
// Should be able to close channel but only once
ASSERT_TRUE(closeLogicalChannel(channel));
ASSERT_FALSE(closeLogicalChannel(channel));
}
static const char* kValidReply = "+CGLA: 144,0,FF403eE23cE12eC11461ed377e85d386a8dfee6b864bd85b0bfaa5af81CA16616e64726f69642e636172726965726170692e637473E30aDB080000000000000000\rOK";
static const char* kCmeError = "+CME ERROR: ";
TEST_F(ModemTest, TransmitLogicalChannel) {
static const struct {
const char* ChannelName;
const char* CommandFormat;
const char* ExpectedReply;
bool FullMatch;
} kData[] = {
// No open channel
{ nullptr, "AT+CGLA=1,10,80CAFF4000", kCmeError, false },
// Open channel, correct format, correct reply, lower 4 bits in class
// shouldn't matter
{ "A00000015141434C00", "AT+CGLA=%d,10,80CAFF4000", kValidReply, true },
{ "A00000015141434C00", "AT+CGLA=%d,10,8FCAFF4000", kValidReply, true },
{ "A00000015141434C00", "AT+CGLA=%d,10,87CAFF4000", kValidReply, true },
{ "A00000015141434C00", "AT+CGLA=%d,10,83CAFF4000", kValidReply, true },
// Correct data but channel name with no matching data
{ "ABCDEF", "AT+CGLA=%d,10,80CAFF4000", kCmeError, false },
// Mismatching length and command size, various combinations
{ "F6238748383", "AT+CGLA=%d,9,80CAFF4000", kCmeError, false },
{ "F6238748383", "AT+CGLA=%d,11,80CAFF4000", kCmeError, false },
{ "F6238748383", "AT+CGLA=%d,10,80CAFF40", kCmeError, false },
// Too short, must be at least 10 characters to be an APDU command
{ "F6238748383", "AT+CGLA=%d,8,80CAFF40", kCmeError, false },
// Open a channel but use an incorrect channel number
{ "F6238748383", "AT+CGLA=10000,10,80CAFF4000", kCmeError, false },
{ "F6238748383", "AT+CGLA=0,10,80CAFF4000", kCmeError, false },
{ "F6238748383", "AT+CGLA=-1,10,80CAFF4000", kCmeError, false },
// Attempt to use an incorrect class or instruction (first two bytes)
{ "F6238748383", "AT+CGLA=%d,10,90CAFF4000", kCmeError, false },
{ "F6238748383", "AT+CGLA=%d,10,80CBFF4000", kCmeError, false },
{ "F6238748383", "AT+CGLA=%d,10,A0DBFF4000", kCmeError, false },
};
static const size_t kNumTests = android::base::arraySize(kData);
for (size_t i = 0; i < kNumTests; ++i) {
int channel = -1;
if (kData[i].ChannelName) {
channel = openLogicalChannel(kData[i].ChannelName);
ASSERT_GE(channel, 1);
}
char command[1024];
if (strstr(kData[i].CommandFormat, "%d") != nullptr) {
// Insert channel number if there's a format argument in there
snprintf(command, sizeof(command), kData[i].CommandFormat, channel);
} else {
snprintf(command, sizeof(command), "%s", kData[i].CommandFormat);
}
command[sizeof(command) - 1] = '\0';
const char* reply = amodem_send(mModem, command);
ASSERT_NE(nullptr, reply);
if (kData[i].FullMatch) {
ASSERT_STREQ(kData[i].ExpectedReply, reply);
} else {
ASSERT_EQ(reply, strstr(reply, kData[i].ExpectedReply));
}
closeLogicalChannel(channel);
}
// Also test that transmission fails on a closed channel
int channel = openLogicalChannel("A00000015141434C00");
ASSERT_GE(channel, 1);
// First transmit something successfully, then repeat the exact same thing
// but with the channel closed
for (int i = 0; i < 2; ++i) {
char command[1024];
snprintf(command, sizeof(command), "AT+CGLA=%d,10,80CAFF4000", channel);
command[sizeof(command) - 1] = '\0';
const char* reply = amodem_send(mModem, command);
if (i == 0) {
ASSERT_STREQ(kValidReply, reply);
closeLogicalChannel(channel);
} else {
ASSERT_EQ(reply, strstr(reply, kCmeError));
}
}
}