mirror of
https://github.com/google/pebble.git
synced 2025-03-26 13:09:06 +00:00
356 lines
13 KiB
C
356 lines
13 KiB
C
/*
|
|
* 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_inbox.h"
|
|
#include "applib/app_message/app_message_internal.h"
|
|
#include "applib/app_message/app_message_receiver.h"
|
|
#include "comm/bt_conn_mgr.h"
|
|
#include "services/common/comm_session/session_receive_router.h"
|
|
#include "kernel/events.h"
|
|
#include "process_management/app_install_types.h"
|
|
|
|
extern const ReceiverImplementation g_app_message_receiver_implementation;
|
|
|
|
static const ReceiverImplementation *s_rcv_imp = &g_app_message_receiver_implementation;
|
|
|
|
#define MAX_HEADER_SIZE (sizeof(AppMessageHeader))
|
|
#define BUFFER_SIZE (sizeof(AppMessagePush))
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Fakes & Stubs
|
|
|
|
#include "fake_kernel_malloc.h"
|
|
#include "fake_system_task.h"
|
|
|
|
#include "stubs_analytics.h"
|
|
#include "stubs_logging.h"
|
|
#include "stubs_mutex.h"
|
|
#include "stubs_passert.h"
|
|
#include "stubs_syscall_internal.h"
|
|
|
|
bool process_manager_send_event_to_process(PebbleTask task, PebbleEvent* e) {
|
|
cl_assert_equal_i(PEBBLE_CALLBACK_EVENT, e->type);
|
|
|
|
// Use fake_system_task as mock implementation:
|
|
system_task_add_callback(e->callback.callback, e->callback.data);
|
|
return true;
|
|
}
|
|
|
|
static void prv_process_events(void) {
|
|
fake_system_task_callbacks_invoke_pending();
|
|
}
|
|
|
|
static AppInbox *s_app_message_inbox;
|
|
AppInbox **app_state_get_app_message_inbox(void) {
|
|
return &s_app_message_inbox;
|
|
}
|
|
|
|
static bool s_communication_timestamp_updated;
|
|
void app_install_mark_prioritized(AppInstallId install_id, bool can_expire) {
|
|
s_communication_timestamp_updated = true;
|
|
}
|
|
|
|
AppInstallId app_manager_get_current_app_id(void) {
|
|
return INSTALL_ID_INVALID;
|
|
}
|
|
|
|
static uint8_t s_app_message_pp_buffer[BUFFER_SIZE];
|
|
static size_t s_app_message_pp_received_length;
|
|
|
|
void app_message_app_protocol_msg_callback(CommSession *session,
|
|
const uint8_t* data, size_t length,
|
|
AppInboxConsumerInfo *consumer_info) {
|
|
cl_assert(length <= BUFFER_SIZE);
|
|
memcpy(s_app_message_pp_buffer, data, length);
|
|
s_app_message_pp_received_length = length;
|
|
}
|
|
|
|
static void prv_protocol_msg_callback(CommSession *session,
|
|
const uint8_t* data, size_t length) {
|
|
app_message_app_protocol_msg_callback(session, data, length, NULL);
|
|
}
|
|
|
|
void app_message_inbox_handle_dropped_messages(uint32_t num_drops) {
|
|
}
|
|
|
|
void comm_session_set_responsiveness(CommSession *session, BtConsumer consumer,
|
|
ResponseTimeState state, uint16_t max_period_secs) {
|
|
}
|
|
|
|
static bool s_kernel_receiver_available;
|
|
static Receiver *s_kernel_receiver;
|
|
static bool s_kernel_receiver_is_receiving;
|
|
static uint8_t s_kernel_receiver_buffer[MAX_HEADER_SIZE];
|
|
static off_t s_kernel_receiver_buffer_idx;
|
|
static bool s_kernel_receiver_finish_called;
|
|
static bool s_kernel_receiver_cleanup_called;
|
|
|
|
static Receiver *prv_default_kernel_receiver_prepare(CommSession *session,
|
|
const PebbleProtocolEndpoint *endpoint,
|
|
size_t total_payload_size) {
|
|
if (!s_kernel_receiver_available) {
|
|
return NULL;
|
|
}
|
|
s_kernel_receiver_is_receiving = true;
|
|
cl_assert_equal_p(endpoint->handler, app_message_app_protocol_system_nack_callback);
|
|
cl_assert(total_payload_size <= MAX_HEADER_SIZE);
|
|
return s_kernel_receiver;
|
|
}
|
|
|
|
static void prv_default_kernel_receiver_write(Receiver *receiver,
|
|
const uint8_t *data, size_t length) {
|
|
cl_assert_equal_p(receiver, s_kernel_receiver);
|
|
memcpy(s_kernel_receiver_buffer + s_kernel_receiver_buffer_idx, data, length);
|
|
s_kernel_receiver_buffer_idx += length;
|
|
cl_assert(s_kernel_receiver_buffer_idx <= MAX_HEADER_SIZE);
|
|
}
|
|
|
|
static void prv_default_kernel_receiver_finish(Receiver *receiver) {
|
|
cl_assert_equal_p(receiver, s_kernel_receiver);
|
|
s_kernel_receiver_is_receiving = false;
|
|
s_kernel_receiver_finish_called = true;
|
|
}
|
|
|
|
static void prv_default_kernel_receiver_cleanup(Receiver *receiver) {
|
|
cl_assert_equal_p(receiver, s_kernel_receiver);
|
|
s_kernel_receiver_is_receiving = false;
|
|
s_kernel_receiver_cleanup_called = true;
|
|
}
|
|
|
|
const ReceiverImplementation g_default_kernel_receiver_implementation = {
|
|
.prepare = prv_default_kernel_receiver_prepare,
|
|
.write = prv_default_kernel_receiver_write,
|
|
.finish = prv_default_kernel_receiver_finish,
|
|
.cleanup = prv_default_kernel_receiver_cleanup,
|
|
};
|
|
|
|
void app_message_app_protocol_system_nack_callback(CommSession *session,
|
|
const uint8_t* data, size_t length) {
|
|
}
|
|
void test_dropped_handler(uint32_t num_dropped_messages) {}
|
|
void test_message_handler(const uint8_t *data, size_t length,
|
|
AppInboxConsumerInfo *consumer_info) {}
|
|
void test_alt_message_handler(const uint8_t *data, size_t length,
|
|
AppInboxConsumerInfo *consumer_info) {}
|
|
void test_alt_dropped_handler(uint32_t num_dropped_messages) {}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Tests
|
|
|
|
static CommSession *s_session = (CommSession *)0xaabbccdd;
|
|
|
|
void test_app_message_receiver__initialize(void) {
|
|
s_kernel_receiver_available = true;
|
|
s_kernel_receiver = (Receiver *)0xffaaffaa;
|
|
s_kernel_receiver_is_receiving = false;
|
|
s_kernel_receiver_finish_called = false;
|
|
s_kernel_receiver_cleanup_called = false;
|
|
s_kernel_receiver_buffer_idx = 0;
|
|
memset(s_kernel_receiver_buffer, 0, sizeof(s_kernel_receiver_buffer));
|
|
s_app_message_pp_received_length = 0;
|
|
memset(s_app_message_pp_buffer, 0, sizeof(s_app_message_pp_buffer));
|
|
fake_kernel_malloc_init();
|
|
fake_kernel_malloc_enable_stats(true);
|
|
s_communication_timestamp_updated = false;
|
|
}
|
|
|
|
void test_app_message_receiver__cleanup(void) {
|
|
fake_system_task_callbacks_cleanup();
|
|
fake_kernel_malloc_deinit();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Forwarding to default system receiver to nack the message
|
|
|
|
static const AppMessagePush s_push = {
|
|
.header = {
|
|
.command = CMD_PUSH,
|
|
.transaction_id = 0xa5,
|
|
},
|
|
};
|
|
|
|
static const PebbleProtocolEndpoint s_app_message_endpoint = (const PebbleProtocolEndpoint) {
|
|
.endpoint_id = APP_MESSAGE_ENDPOINT_ID,
|
|
.handler = prv_protocol_msg_callback,
|
|
.access_mask = PebbleProtocolAccessAny,
|
|
.receiver_imp = &g_app_message_receiver_implementation,
|
|
.receiver_opt = NULL,
|
|
};
|
|
|
|
void test_app_message_receiver__receive_push_but_inbox_not_opened(void) {
|
|
Receiver *r = s_rcv_imp->prepare(s_session, &s_app_message_endpoint,
|
|
sizeof(AppMessagePush));
|
|
cl_assert(r != NULL);
|
|
cl_assert_equal_b(true, s_kernel_receiver_is_receiving);
|
|
|
|
s_rcv_imp->write(r, (const uint8_t *)&s_push, sizeof(s_push));
|
|
|
|
// Expect only up to MAX_HEADER_SIZE bytes has been written:
|
|
cl_assert_equal_i(s_kernel_receiver_buffer_idx, MAX_HEADER_SIZE);
|
|
// Check that the header is received correctly:
|
|
cl_assert_equal_m(s_kernel_receiver_buffer, &s_push, MAX_HEADER_SIZE);
|
|
|
|
s_rcv_imp->finish(r);
|
|
prv_process_events();
|
|
cl_assert_equal_b(false, s_kernel_receiver_is_receiving);
|
|
cl_assert_equal_b(true, s_kernel_receiver_finish_called);
|
|
}
|
|
|
|
void test_app_message_receiver__receive_push_but_inbox_not_opened_then_cleanup(void) {
|
|
Receiver *r = s_rcv_imp->prepare(s_session, &s_app_message_endpoint,
|
|
sizeof(AppMessagePush));
|
|
s_rcv_imp->write(r, (const uint8_t *)&s_push, sizeof(s_push));
|
|
|
|
s_rcv_imp->cleanup(r);
|
|
cl_assert_equal_b(false, s_kernel_receiver_is_receiving);
|
|
cl_assert_equal_b(true, s_kernel_receiver_cleanup_called);
|
|
}
|
|
|
|
void test_app_message_receiver__receive_push_but_inbox_not_opened_kernel_oom(void) {
|
|
fake_kernel_malloc_set_largest_free_block(0);
|
|
|
|
Receiver *r = s_rcv_imp->prepare(s_session, &s_app_message_endpoint,
|
|
sizeof(AppMessagePush));
|
|
cl_assert_equal_p(r, NULL);
|
|
cl_assert_equal_b(false, s_kernel_receiver_is_receiving);
|
|
}
|
|
|
|
void test_app_message_receiver__receive_push_but_inbox_not_opened_no_kernel_receiver(void) {
|
|
fake_kernel_malloc_mark();
|
|
s_kernel_receiver_available = false;
|
|
Receiver *r = s_rcv_imp->prepare(s_session, &s_app_message_endpoint,
|
|
sizeof(AppMessagePush));
|
|
cl_assert_equal_p(r, NULL);
|
|
cl_assert_equal_b(false, s_kernel_receiver_is_receiving);
|
|
fake_kernel_malloc_mark_assert_equal();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Normal flow: writing message to app message inbox
|
|
|
|
static Receiver *prv_create_inbox_prepare_and_write(void) {
|
|
cl_assert_equal_b(true, app_message_receiver_open(sizeof(AppMessagePush)));
|
|
Receiver *r = s_rcv_imp->prepare(s_session, &s_app_message_endpoint,
|
|
sizeof(AppMessagePush));
|
|
cl_assert(r != NULL);
|
|
|
|
s_rcv_imp->write(r, (const uint8_t *)&s_push, sizeof(s_push));
|
|
return r;
|
|
}
|
|
|
|
static void prv_destroy_inbox(void) {
|
|
app_message_receiver_close();
|
|
}
|
|
|
|
void test_app_message_receiver__receive_push(void) {
|
|
Receiver *r = prv_create_inbox_prepare_and_write();
|
|
|
|
s_rcv_imp->finish(r);
|
|
prv_process_events();
|
|
cl_assert_equal_b(false, s_kernel_receiver_is_receiving);
|
|
cl_assert_equal_b(false, s_kernel_receiver_finish_called);
|
|
|
|
cl_assert_equal_m(&s_push, s_app_message_pp_buffer, sizeof(s_push));
|
|
cl_assert_equal_i(s_app_message_pp_received_length, sizeof(s_push));
|
|
|
|
cl_assert_equal_b(true, s_communication_timestamp_updated);
|
|
|
|
prv_destroy_inbox();
|
|
}
|
|
|
|
void test_app_message_receiver__receive_push_then_cleanup(void) {
|
|
Receiver *r = prv_create_inbox_prepare_and_write();
|
|
|
|
s_rcv_imp->cleanup(r);
|
|
cl_assert_equal_b(false, s_kernel_receiver_is_receiving);
|
|
cl_assert_equal_b(false, s_kernel_receiver_finish_called);
|
|
|
|
cl_assert_equal_i(s_app_message_pp_received_length, 0);
|
|
|
|
cl_assert_equal_b(true, s_communication_timestamp_updated);
|
|
|
|
prv_destroy_inbox();
|
|
}
|
|
|
|
void test_app_message_receiver__receive_push_buffer_overflow(void) {
|
|
cl_assert_equal_b(true, app_message_receiver_open(sizeof(AppMessagePush)));
|
|
// Write an ACK, we should be able to fit one (N)ACK in addition to the Push message:
|
|
AppMessageAck ack = {};
|
|
Receiver *r = s_rcv_imp->prepare(s_session, &s_app_message_endpoint,
|
|
sizeof(ack));
|
|
cl_assert(r != NULL);
|
|
s_rcv_imp->write(r, (const uint8_t *)&ack, sizeof(ack));
|
|
s_rcv_imp->finish(r);
|
|
|
|
cl_assert_equal_b(false, s_kernel_receiver_finish_called);
|
|
cl_assert_equal_b(true, s_kernel_receiver_cleanup_called);
|
|
s_app_message_pp_received_length = 0;
|
|
s_kernel_receiver_buffer_idx = 0;
|
|
|
|
// Write a Push:
|
|
r = s_rcv_imp->prepare(s_session, &s_app_message_endpoint,
|
|
sizeof(AppMessagePush));
|
|
cl_assert(r != NULL);
|
|
s_rcv_imp->write(r, (const uint8_t *)&s_push, sizeof(s_push));
|
|
|
|
// Write some more, doesn't fit in the buffer:
|
|
s_rcv_imp->write(r, (const uint8_t *)&s_push, sizeof(s_push));
|
|
s_rcv_imp->finish(r);
|
|
|
|
prv_process_events();
|
|
|
|
// Header fwd to default system receiver should have finished, so it can be nacked:
|
|
cl_assert_equal_b(true, s_kernel_receiver_finish_called);
|
|
|
|
// Only Ack is received:
|
|
cl_assert_equal_i(s_app_message_pp_received_length, sizeof(AppMessageAck));
|
|
prv_destroy_inbox();
|
|
}
|
|
|
|
// Covers race as described here: PBL-41464
|
|
// 1. A part of a big app message is being received, causing it to get received in chunks.
|
|
// It's not received completely yet.
|
|
// 2. app_message_outbox_closed is called.
|
|
// 3. app_message_outbox_open is called, resetting the receiver state.
|
|
// 4. Next chunk comes in. We used to assert: the "writer" (receiver state) was not NULL.
|
|
// Fix: just eat the message and fail the app message by letting the KernelReceiver NACK it.
|
|
void test_app_message_receiver__receive_multi_chunk_push_while_open_close_toggle(void) {
|
|
cl_assert_equal_b(true, app_message_receiver_open(sizeof(AppMessagePush)));
|
|
|
|
Receiver *r = s_rcv_imp->prepare(s_session, &s_app_message_endpoint,
|
|
sizeof(AppMessagePush));
|
|
cl_assert(r != NULL);
|
|
|
|
// Receive only first byte of the push message:
|
|
s_rcv_imp->write(r, (const uint8_t *)&s_push, 1);
|
|
|
|
// Close app message and open again:
|
|
app_message_receiver_close();
|
|
cl_assert_equal_b(true, app_message_receiver_open(sizeof(AppMessagePush)));
|
|
|
|
// Receive the remainder of the push message:
|
|
s_rcv_imp->write(r, ((const uint8_t *)&s_push) + 1, sizeof(s_push) - 1);
|
|
s_rcv_imp->finish(r);
|
|
|
|
// Header fwd to default system receiver should have finished, so it can be nacked:
|
|
prv_process_events();
|
|
cl_assert_equal_b(false, s_kernel_receiver_is_receiving);
|
|
cl_assert_equal_b(true, s_kernel_receiver_finish_called);
|
|
|
|
prv_destroy_inbox();
|
|
}
|