blob: 01000c625c5b155df0d5dfc42e0088c6999ffc2d [file] [log] [blame]
/*
* Copyright (C) 2019 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 <app_mgmt_port_consts.h>
#include <app_mgmt_test.h>
#include <assert.h>
#include <lib/tipc/tipc.h>
#include <lib/unittest/unittest.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <trusty_ipc.h>
#include <trusty_unittest.h>
#include <uapi/err.h>
#define TLOG_TAG "app-mgmt-test-client"
#define PORT_BASE "com.android.appmgmt-unittest.appmngr"
/*
* These are expected to occur/elapse in passing test cases and as such there is
* a trade off between the degree of confidence provided by the tests and the
* runtime of the tests.
*/
#define EXPECTED_TIMEOUT_MS 500
#define WAIT_FOR_APP_SLEEP_NS 500000000
/*
* These tests makes use of one client TA (this file) and 4 test server TAs:
* boot-start-srv, never-start-srv, restart-srv and port-tart-srv.
*
* boot-start-srv:
* - Starts at boot
* - Creates BOOT_START_PORT
* - Never exits
* - Does not restart at exit
*
* never-start-srv:
* - Should never be started
* - Creates NEVER_START_PORT (if ever started i.e. failure case)
* - Exits after receiving a connection
* - Restarts after exiting
*
* restart-srv:
* - Starts at boot
* - Creates RESTART_PORT
* - Exits after receiving a connection
* - Restarts after exiting
*
* start-port-srv:
* - Doesn't start at boot
* - Starts on connections to START_PORT
* - Creates START_PORT, CTRL_PORT and SHUTDOWN_PORT
* - Exits after receiving a CMD_EXIT command on START_PORT or a connection on
* SHUTDOWN_PORT
* - Does not restart at exit
*
*/
static bool port_start_srv_running(void) {
int rc;
trusty_nanosleep(0, 0, WAIT_FOR_APP_SLEEP_NS);
rc = connect(CTRL_PORT, IPC_CONNECT_ASYNC);
close((handle_t)rc);
return rc >= 0;
}
static void chan_send_cmd(handle_t chan, uint8_t cmd) {
uint8_t rsp;
uevent_t uevt;
if (HasFailure())
return;
ASSERT_EQ(sizeof(cmd), tipc_send1(chan, &cmd, sizeof(cmd)));
ASSERT_EQ(NO_ERROR, wait(chan, &uevt, INFINITE_TIME));
ASSERT_NE(0, uevt.event & IPC_HANDLE_POLL_MSG);
ASSERT_EQ(sizeof(rsp), tipc_recv1(chan, sizeof(rsp), &rsp, sizeof(rsp)));
ASSERT_EQ(RSP_OK, rsp);
test_abort:
return;
}
typedef enum {
/* Accepted on connect and served by port-start-srv */
MAIN_CHAN,
/* Not accepted on connect. Put on pending list when established */
PEND_CHAN,
/* Not accepted on connect. Put on waiting list when established */
WAIT_CHAN,
CHAN_COUNT,
} chan_idx_t;
typedef struct {
handle_t chans[CHAN_COUNT];
} AppMgrPortStart_t;
static void send_cmd(AppMgrPortStart_t* state, chan_idx_t idx, uint8_t cmd) {
assert(idx < CHAN_COUNT);
if (HasFailure())
return;
chan_send_cmd(state->chans[idx], cmd);
test_abort:
return;
}
static void establish_unhandled_channel(AppMgrPortStart_t* state,
chan_idx_t idx) {
int rc;
uevent_t uevt;
handle_t chan = INVALID_IPC_HANDLE;
assert(idx < CHAN_COUNT);
if (HasFailure())
return;
rc = connect(START_PORT, IPC_CONNECT_ASYNC);
ASSERT_GE(rc, 0);
chan = (handle_t)rc;
/* Make sure port-start-srv does not accept the connection */
ASSERT_EQ(ERR_TIMED_OUT, wait(chan, &uevt, EXPECTED_TIMEOUT_MS));
state->chans[idx] = chan;
test_abort:
return;
}
static void close_channel(AppMgrPortStart_t* state, chan_idx_t idx) {
assert(idx < CHAN_COUNT);
if (HasFailure())
return;
close(state->chans[idx]);
state->chans[idx] = INVALID_IPC_HANDLE;
}
static void send_exit(AppMgrPortStart_t* state, chan_idx_t idx) {
assert(idx < CHAN_COUNT);
if (HasFailure())
return;
send_cmd(state, idx, CMD_EXIT);
close_channel(state, idx);
}
static void wait_and_exit(AppMgrPortStart_t* state, chan_idx_t idx) {
uevent_t uevt;
assert(idx < CHAN_COUNT);
if (HasFailure())
return;
ASSERT_EQ(NO_ERROR, wait(state->chans[idx], &uevt, INFINITE_TIME));
ASSERT_NE(0, IPC_HANDLE_POLL_READY & uevt.event);
send_exit(state, idx);
test_abort:
return;
}
static void AppMgrPortStart_SetUp(AppMgrPortStart_t* state) {
int rc;
uevent_t uevt;
handle_t chan;
for (size_t i = 0; i < CHAN_COUNT; i++) {
state->chans[i] = INVALID_IPC_HANDLE;
}
/* Shutdown port-start-srv in case it is running from a previous test */
rc = connect(SHUTDOWN_PORT, IPC_CONNECT_ASYNC);
if (rc > 0) {
/* SHUTDOWN_PORT exists so the srv was running. Wait for it to exit */
chan = (handle_t)rc;
rc = wait(chan, &uevt, INFINITE_TIME);
close(chan);
ASSERT_GE(rc, 0);
ASSERT_NE(0, uevt.event & IPC_HANDLE_POLL_HUP);
}
/* port-start-srv should not be running */
ASSERT_EQ(false, port_start_srv_running());
/* Start and connect to port-start-srv */
rc = connect(START_PORT, 0);
ASSERT_GE(rc, 0);
state->chans[MAIN_CHAN] = (handle_t)rc;
test_abort:
return;
}
static void AppMgrPortStart_TearDown(AppMgrPortStart_t* state) {
ASSERT_EQ(false, HasFailure());
/* port-start-srv should not be running at the end of a test */
ASSERT_EQ(false, port_start_srv_running());
for (size_t i = 0; i < CHAN_COUNT; i++) {
ASSERT_EQ(INVALID_IPC_HANDLE, state->chans[i]);
}
test_abort:
for (size_t i = 0; i < CHAN_COUNT; i++) {
close(state->chans[i]);
}
}
/* Apps with deferred start should not start at boot */
TEST(AppMgrBoot, BootStartNegative) {
int rc;
/* never-start-srv should not be running */
rc = connect(NEVER_START_PORT, IPC_CONNECT_ASYNC);
EXPECT_LT(rc, 0);
close((handle_t)rc);
}
/* Apps without deferred should start at boot */
TEST(AppMgrBoot, BootStartPositive) {
int rc;
/* boot-start-srv should be running from boot */
rc = connect(BOOT_START_PORT, IPC_CONNECT_ASYNC);
EXPECT_GE(rc, 0);
close((handle_t)rc);
}
/* Apps with automatic restart should restart after exiting */
TEST(AppMgrRestart, AppRestartPositive) {
int rc;
uevent_t uevt;
handle_t chan = INVALID_IPC_HANDLE;
/* restart-srv should be running from boot or a previous restart */
rc = connect(RESTART_PORT, IPC_CONNECT_ASYNC | IPC_CONNECT_WAIT_FOR_PORT);
ASSERT_GE(rc, 0);
/* Wait for restart-srv to initiate shutdown */
chan = (handle_t)rc;
ASSERT_EQ(NO_ERROR, wait(chan, &uevt, INFINITE_TIME));
ASSERT_NE(0, IPC_HANDLE_POLL_HUP & uevt.event);
close(chan);
/* restart-srv should eventually restart */
rc = connect(RESTART_PORT, IPC_CONNECT_ASYNC | IPC_CONNECT_WAIT_FOR_PORT);
ASSERT_GE(rc, 0);
chan = (handle_t)rc;
test_abort:
close(chan);
}
/*
* Apps without automatic restart should not restart after exiting
* Start ports should start an app on connection
*/
TEST(AppMgrRestart, AppRestartNegativePortStartPositive) {
int rc;
handle_t chan = INVALID_IPC_HANDLE;
/* Start and connect to port-start-srv */
rc = connect(START_PORT, 0);
ASSERT_GE(rc, 0);
chan = (handle_t)rc;
/* Shutdown port-start-srv */
chan_send_cmd(chan, CMD_EXIT);
ASSERT_EQ(false, HasFailure());
/* port-start-srv should not restart */
ASSERT_EQ(false, port_start_srv_running());
test_abort:
close((handle_t)rc);
}
/* Regular ports should not start an app on connection */
TEST(AppMgrPortStartNegative, PortStartNegative) {
int rc;
/* A connection to CTRL_PORT should not start port-start-srv */
rc = connect(CTRL_PORT, IPC_CONNECT_ASYNC);
EXPECT_LT(rc, 0);
close((handle_t)rc);
}
/* Start ports with closed pending connections should not start an app */
TEST_F(AppMgrPortStart, PortStartPendingNegative) {
/* Create a pending connection */
establish_unhandled_channel(_state, PEND_CHAN);
/* Close the pending connection */
close_channel(_state, PEND_CHAN);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
}
/* Start ports with pending connections should start an app */
TEST_F(AppMgrPortStart, PortStartPendingPositive) {
/* Create a pending connection */
establish_unhandled_channel(_state, PEND_CHAN);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
/*
* Wait for port-start-srv to restart due to the pending connection and then
* shut it down
*/
wait_and_exit(_state, PEND_CHAN);
}
/* Closed connections waiting for a start port should not start an app */
TEST_F(AppMgrPortStart, PortStartWaitingNegative) {
/* Make port-start-srv close START_PORT */
send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT);
/* Create a waiting connection */
establish_unhandled_channel(_state, WAIT_CHAN);
/* Close the waiting connection */
close_channel(_state, WAIT_CHAN);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
}
/* Connections waiting for a start port should start an app */
TEST_F(AppMgrPortStart, PortStartWaitingPositive) {
/* Make port-start-srv close START_PORT */
send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT);
/* Create a waiting connection */
establish_unhandled_channel(_state, WAIT_CHAN);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
/*
* Wait for port-start-srv to restart due to the waiting connection and then
* shut it down
*/
wait_and_exit(_state, WAIT_CHAN);
}
/*
* Closed waiting connections that were pending on a start port should not start
* an app
*/
TEST_F(AppMgrPortStart, PortStartPendingToWaitingNegative) {
/* Create a pending connection */
establish_unhandled_channel(_state, PEND_CHAN);
/*
* Make port-start-srv close START_PORT (the pending connection becomes
* waiting)
*/
send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT);
/* Close the waiting connection */
close_channel(_state, PEND_CHAN);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
}
/*
* Waiting connections that were pending on a start port should start an app
*/
TEST_F(AppMgrPortStart, PortStartPendingToWaitingPositive) {
/* Create a pending connection */
establish_unhandled_channel(_state, PEND_CHAN);
/*
* Make port-start-srv close START_PORT (the pending connection becomes
* waiting)
*/
send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
/*
* Wait for port-start-srv to restart due to the waiting connection and then
* shut it down
*/
wait_and_exit(_state, PEND_CHAN);
}
/*
* Start ports with closed pending connections that were waiting for the port
* should not start an app
*/
TEST_F(AppMgrPortStart, PortStartWaitingToPendingNegative) {
/* Make port-start-srv close START_PORT */
send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT);
/* Create a waiting connection */
establish_unhandled_channel(_state, WAIT_CHAN);
/*
* Make port-start-srv open START_PORT (the waiting connection becomes
* pending)
*/
send_cmd(_state, MAIN_CHAN, CMD_OPEN_PORT);
/* Close the pending connection */
close_channel(_state, WAIT_CHAN);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
}
/*
* Start ports with pending connections that were waiting for the port should
* start an app
*/
TEST_F(AppMgrPortStart, PortStartWaitingToPendingPositive) {
/* Make port-start-srv close START_PORT */
send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT);
/* Create a waiting connection */
establish_unhandled_channel(_state, WAIT_CHAN);
/*
* Make port-start-srv open START_PORT (the waiting connection becomes
* pending)
*/
send_cmd(_state, MAIN_CHAN, CMD_OPEN_PORT);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
/*
* Wait for port-start-srv to restart due to the pending connection and then
* shut it down
*/
wait_and_exit(_state, WAIT_CHAN);
}
/*
* Closed connections waiting for a start port with closed pending connections
* should not start an app
*/
TEST_F(AppMgrPortStart, PortStartPendingWaitingNegative) {
/* Create a pending connection */
establish_unhandled_channel(_state, PEND_CHAN);
/*
* Make port-start-srv close START_PORT (the pending connection becomes
* waiting)
*/
send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT);
/* Create a waiting connection */
establish_unhandled_channel(_state, WAIT_CHAN);
/* Close the first waiting connection */
close_channel(_state, PEND_CHAN);
/* Close the second waiting connection */
close_channel(_state, WAIT_CHAN);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
}
/*
* Connections waiting for a start port with pending connections should start
* an app
*/
TEST_F(AppMgrPortStart, PortStartPendingWaitingPositive) {
/* Create a pending connection */
establish_unhandled_channel(_state, PEND_CHAN);
/*
* Make port-start-srv close START_PORT (the pending connection becomes
* waiting)
*/
send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT);
/* Create a waiting connection */
establish_unhandled_channel(_state, WAIT_CHAN);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
/*
* Wait for port-start-srv to restart due to the first waiting connection
* and then shut it down
*/
wait_and_exit(_state, PEND_CHAN);
/*
* wait for port-start-srv to restart due to the second waiting connection
* and then shut it down
*/
wait_and_exit(_state, WAIT_CHAN);
}
/*
* Connections waiting for a start port with closed pending connections should
* start an app
*/
TEST_F(AppMgrPortStart, PortStartPendingClosedWaitingPositive) {
/* Create a pending connection */
establish_unhandled_channel(_state, PEND_CHAN);
/*
* Make port-start-srv close START_PORT (the pending connection becomes
* waiting)
*/
send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT);
/* Create a waiting connection */
establish_unhandled_channel(_state, WAIT_CHAN);
/* Close the first waiting connection */
close_channel(_state, PEND_CHAN);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
/*
* wait for port-start-srv to restart due to the second waiting connection
* and then shut it down
*/
wait_and_exit(_state, WAIT_CHAN);
}
/*
* Start ports with pending connections and with closed connections waiting for
* the port should start an app
*/
TEST_F(AppMgrPortStart, PortStartPendingWaitingClosedPositive) {
/* Create a pending connection */
establish_unhandled_channel(_state, PEND_CHAN);
/*
* Make port-start-srv close START_PORT (the pending connection becomes
* waiting)
*/
send_cmd(_state, MAIN_CHAN, CMD_CLOSE_PORT);
/* Create a waiting connection */
establish_unhandled_channel(_state, WAIT_CHAN);
/* Close the second waiting connection */
close_channel(_state, WAIT_CHAN);
/* Close the main channel and shutdown port-start-srv */
send_exit(_state, MAIN_CHAN);
/*
* wait for port-start-srv to restart due to the first waiting connection
* and then shut it down
*/
wait_and_exit(_state, PEND_CHAN);
}
static bool run_appmngr_tests(struct unittest* test) {
return RUN_ALL_TESTS();
}
static bool run_appmngr_stress_tests(struct unittest* test) {
while (RUN_ALL_TESTS()) {
}
return false;
}
int main(void) {
static struct unittest appmgmt_unittests[] = {
{
.port_name = PORT_BASE,
.run_test = run_appmngr_tests,
},
{
.port_name = PORT_BASE ".stress",
.run_test = run_appmngr_stress_tests,
},
};
struct unittest* unittests[countof(appmgmt_unittests)];
for (size_t i = 0; i < countof(appmgmt_unittests); i++)
unittests[i] = &appmgmt_unittests[i];
return unittest_main(unittests, countof(unittests));
}