/* * Copyright 2024 Google LLC * * 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 "clar.h" #include "applib/app_message/app_message_internal.h" #include "process_management/app_run_state.h" #include "process_management/launcher_app_message.h" #include "services/common/comm_session/session_internal.h" #include "system/passert.h" #include "util/dict.h" #include "util/uuid.h" extern void launcher_app_message_reset(void); extern void launcher_app_message_protocol_msg_callback_deprecated(CommSession *session, const uint8_t* data, size_t length); // Fakes //////////////////////////////////// #include "fake_app_manager.h" #include "fake_pbl_malloc.h" #include "fake_session.h" #include "fake_system_task.h" // Stubs //////////////////////////////////// #include "stubs_bt_lock.h" #include "stubs_hexdump.h" #include "stubs_logging.h" #include "stubs_passert.h" #include "stubs_rand_ptr.h" AppRunStateCommand s_last_cmd; Transport *s_transport; CommSession *s_session; #define APP_UUID_RAW 0x13, 0xEC, 0xC6, 0x7C, 0xCC, 0xB4, 0x4A, 0x96, \ 0x9E, 0xA7, 0x50, 0xE5, 0x09, 0xCA, 0xF7, 0x3A #define LAUNCHER_MESSAGE_ENDPOINT_ID (0x31) #define RUN_STATE_KEY (1) #define STATE_FETCH_KEY (2) #define INVALID_KEY (0xffffffff) #define RUNNING (1) #define NOT_RUNNING (0) #define TRANSACTION_ID (0xA5) #define assert_ack(ack) \ fake_comm_session_process_send_next(); \ const AppMessageAck ack_message = { \ .header = { \ .command = ack ? CMD_ACK : CMD_NACK, \ .transaction_id = TRANSACTION_ID, \ }, \ }; \ fake_transport_assert_sent(s_transport, 0, LAUNCHER_MESSAGE_ENDPOINT_ID, \ (const uint8_t *)&ack_message, sizeof(ack_message)); void app_run_state_command(CommSession *session, AppRunStateCommand cmd, const Uuid *uuid) { s_last_cmd = cmd; Uuid expected_uuid = {APP_UUID_RAW}; cl_assert_equal_b(uuid, &expected_uuid); if (cmd == APP_RUN_STATE_STATUS_COMMAND) { launcher_app_message_send_app_state_deprecated(uuid, RUNNING); } } // Helpers //////////////////////////////////// static const uint8_t *prv_build_push_message(uint32_t key, uint8_t value, uint32_t *size) { // Using static buffer is OK because tests are single-threaded static uint8_t buffer[sizeof(AppMessagePush) + sizeof(Tuple) + sizeof(uint8_t)]; AppMessagePush *push_message = (AppMessagePush *)buffer; *push_message = (const AppMessagePush) { .header = { .command = 0x01, // Push .transaction_id = TRANSACTION_ID, }, .uuid = { APP_UUID_RAW }, }; *size = sizeof(Dictionary) + sizeof(Tuple) + sizeof(uint8_t); const Tuplet tuplet = TupletInteger(key, value); cl_assert_equal_i(DICT_OK, dict_serialize_tuplets_to_buffer(&tuplet, 1, (uint8_t *)&push_message->dictionary, size)); // Including sizeof(AppMessagePush): *size = sizeof(buffer); return buffer; } static void prv_receive(uint32_t key, uint8_t value) { uint32_t length = 0; const uint8_t *msg= prv_build_push_message(key, value, &length); launcher_app_message_protocol_msg_callback_deprecated(s_session, msg, length); } // Tests //////////////////////////////////// void test_launcher_app_message__initialize(void) { launcher_app_message_reset(); s_last_cmd = APP_RUN_STATE_INVALID_COMMAND; fake_comm_session_init(); s_transport = fake_transport_create(TransportDestinationSystem, NULL, NULL); s_session = fake_transport_set_connected(s_transport, true /* connected */); } void test_launcher_app_message__cleanup(void) { fake_transport_destroy(s_transport); s_transport = NULL; s_session = NULL; fake_comm_session_cleanup(); fake_system_task_callbacks_cleanup(); } void test_launcher_app_message__ingore_too_short_message(void) { uint8_t too_short = 0; launcher_app_message_protocol_msg_callback_deprecated(s_session, &too_short, sizeof(too_short)); fake_comm_session_process_send_next(); fake_transport_assert_nothing_sent(s_transport); } void test_launcher_app_message__receive_unknown_key(void) { prv_receive(INVALID_KEY, 0); assert_ack(false); } void test_launcher_app_message__receive_push_start(void) { prv_receive(RUN_STATE_KEY, RUNNING); assert_ack(true); cl_assert_equal_i(APP_RUN_STATE_RUN_COMMAND, s_last_cmd); } void test_launcher_app_message__receive_push_stop(void) { prv_receive(RUN_STATE_KEY, NOT_RUNNING); assert_ack(true); cl_assert_equal_i(APP_RUN_STATE_STOP_COMMAND, s_last_cmd); } void test_launcher_app_message__receive_push_fetch_request(void) { prv_receive(STATE_FETCH_KEY, RUNNING); assert_ack(true); cl_assert_equal_i(APP_RUN_STATE_STATUS_COMMAND, s_last_cmd); } void test_launcher_app_message__ignore_acks(void) { const AppMessageAck ack_message = { .header = { .command = CMD_ACK, .transaction_id = TRANSACTION_ID, }, }; launcher_app_message_protocol_msg_callback_deprecated(s_session, (const uint8_t *)&ack_message, sizeof(ack_message)); fake_comm_session_process_send_next(); fake_transport_assert_nothing_sent(s_transport); } void test_launcher_app_message__send_app_state(void) { Uuid uuid = {APP_UUID_RAW}; bool running = true; launcher_app_message_send_app_state_deprecated(&uuid, running); // Even though the Launcher App Message documentation states that the value is a uint8_t, // the original implementation used uint32_t for the outbound pushes... Let's keep that "bug": uint8_t buffer[sizeof(AppMessagePush) + sizeof(Tuple) + sizeof(uint32_t)]; AppMessagePush *push_message = (AppMessagePush *)buffer; *push_message = (const AppMessagePush) { .header = { .command = CMD_PUSH, .transaction_id = 0, }, .uuid = {APP_UUID_RAW}, }; uint32_t size = sizeof(Dictionary) + sizeof(Tuple) + sizeof(uint32_t); const Tuplet tuplet = TupletInteger(RUN_STATE_KEY, (uint32_t) (running ? RUNNING : NOT_RUNNING)); PBL_ASSERTN(DICT_OK == dict_serialize_tuplets_to_buffer(&tuplet, 1, (uint8_t *)&push_message->dictionary, &size)); fake_comm_session_process_send_next(); fake_transport_assert_sent(s_transport, 0, LAUNCHER_MESSAGE_ENDPOINT_ID, buffer, sizeof(buffer)); }