mirror of https://github.com/google/pebble
241 lines
8.5 KiB
C
241 lines
8.5 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 "applib/app_message/app_message.h"
|
|
#include "applib/app_message/app_message_internal.h"
|
|
#include "kernel/pbl_malloc.h"
|
|
#include "process_state/app_state/app_state.h"
|
|
#include "services/common/comm_session/protocol.h"
|
|
#include "syscall/syscall.h"
|
|
#include "system/logging.h"
|
|
|
|
// -------- Initialization ---------------------------------------------------------------------- //
|
|
|
|
void app_message_init(void) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
*app_message_ctx = (const AppMessageCtx) {};
|
|
}
|
|
|
|
// -------- Pebble Protocol Handlers ------------------------------------------------------------ //
|
|
|
|
static bool prv_has_invalid_header_length(size_t length) {
|
|
if (length < sizeof(AppMessageHeader)) {
|
|
PBL_LOG(LOG_LEVEL_ERROR, "Too short");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//! The new implementation uses up to 72 bytes more stack space than the previous implementation.
|
|
//! Might need to do some extra work to get a "thinner" stack, if this causes issues.
|
|
//! Executes on App task.
|
|
void app_message_app_protocol_msg_callback(CommSession *session,
|
|
const uint8_t* data, size_t length,
|
|
AppInboxConsumerInfo *consumer_info) {
|
|
if (prv_has_invalid_header_length(length)) {
|
|
return;
|
|
}
|
|
|
|
AppMessageHeader *message = (AppMessageHeader *) data;
|
|
switch (message->command) {
|
|
|
|
case CMD_PUSH:
|
|
// Incoming message:
|
|
app_message_inbox_receive(session, (AppMessagePush *) message, length, consumer_info);
|
|
return;
|
|
|
|
case CMD_REQUEST:
|
|
// Incoming request for an update push:
|
|
// TODO PBL-1636: decide to implement CMD_REQUEST, or remove it
|
|
return;
|
|
|
|
case CMD_ACK:
|
|
case CMD_NACK:
|
|
// Received ACK/NACK in response to previously pushed update:
|
|
app_message_out_handle_ack_nack_received(message);
|
|
return;
|
|
|
|
default:
|
|
PBL_LOG(LOG_LEVEL_ERROR, "Unknown Cmd 0x%x", message->command);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//! Executes on KernelBG, sends back NACK on behalf of the app if it is not able to do so.
|
|
//! Note that app_message_receiver_dropped_handler will also get called on the App task,
|
|
//! to report the number of missed messages.
|
|
void app_message_app_protocol_system_nack_callback(CommSession *session,
|
|
const uint8_t* data, size_t length) {
|
|
if (prv_has_invalid_header_length(length)) {
|
|
return;
|
|
}
|
|
AppMessageHeader *message = (AppMessageHeader *) data;
|
|
if (message->command != CMD_PUSH) {
|
|
return;
|
|
}
|
|
app_message_inbox_send_ack_nack_reply(session, message->transaction_id, CMD_NACK);
|
|
}
|
|
|
|
// -------- Developer Interface ----------------------------------------------------------------- //
|
|
|
|
void *app_message_get_context(void) {
|
|
return app_state_get_app_message_ctx()->inbox.user_context;
|
|
}
|
|
|
|
void *app_message_set_context(void *context) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
void *retval = app_message_ctx->inbox.user_context;
|
|
app_message_ctx->inbox.user_context = context;
|
|
app_message_ctx->outbox.user_context = context;
|
|
return retval;
|
|
}
|
|
|
|
AppMessageInboxReceived app_message_register_inbox_received(
|
|
AppMessageInboxReceived received_callback) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
AppMessageInboxReceived retval = app_message_ctx->inbox.received_callback;
|
|
app_message_ctx->inbox.received_callback = received_callback;
|
|
return retval;
|
|
}
|
|
|
|
AppMessageInboxDropped app_message_register_inbox_dropped(AppMessageInboxDropped dropped_callback) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
AppMessageInboxDropped retval = app_message_ctx->inbox.dropped_callback;
|
|
app_message_ctx->inbox.dropped_callback = dropped_callback;
|
|
return retval;
|
|
}
|
|
|
|
AppMessageOutboxSent app_message_register_outbox_sent(AppMessageOutboxSent sent_callback) {
|
|
AppMessageOutboxSent retval = app_state_get_app_message_ctx()->outbox.sent_callback;
|
|
app_state_get_app_message_ctx()->outbox.sent_callback = sent_callback;
|
|
return retval;
|
|
}
|
|
|
|
AppMessageOutboxFailed app_message_register_outbox_failed(AppMessageOutboxFailed failed_callback) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
AppMessageOutboxFailed retval = app_message_ctx->outbox.failed_callback;
|
|
app_message_ctx->outbox.failed_callback = failed_callback;
|
|
return retval;
|
|
}
|
|
|
|
void app_message_deregister_callbacks(void) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
app_message_ctx->inbox.received_callback = NULL;
|
|
app_message_ctx->inbox.dropped_callback = NULL;
|
|
app_message_ctx->inbox.user_context = NULL;
|
|
app_message_ctx->outbox.sent_callback = NULL;
|
|
app_message_ctx->outbox.failed_callback = NULL;
|
|
app_message_ctx->outbox.user_context = NULL;
|
|
}
|
|
|
|
static bool prv_supports_8k(void) {
|
|
if (!sys_app_pp_has_capability(CommSessionAppMessage8kSupport)) {
|
|
return false;
|
|
}
|
|
const Version app_sdk_version = sys_get_current_app_sdk_version();
|
|
const Version sdk_version_8k_messages_enabled = (const Version) { 0x05, 0x3f };
|
|
return (version_compare(sdk_version_8k_messages_enabled, app_sdk_version) <= 0);
|
|
}
|
|
|
|
uint32_t app_message_inbox_size_maximum(void) {
|
|
if (prv_supports_8k()) {
|
|
// New behavior, allow up to one large 8K byte array per message:
|
|
return (APP_MSG_8K_DICT_SIZE);
|
|
} else {
|
|
// Legacy behavior:
|
|
if (sys_get_current_app_is_js_allowed()) {
|
|
return (COMM_PRIVATE_MAX_INBOUND_PAYLOAD_SIZE - APP_MSG_HDR_OVRHD_SIZE);
|
|
} else {
|
|
return (COMM_PUBLIC_MAX_INBOUND_PAYLOAD_SIZE - APP_MSG_HDR_OVRHD_SIZE);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t app_message_outbox_size_maximum(void) {
|
|
if (prv_supports_8k()) {
|
|
return (APP_MSG_8K_DICT_SIZE);
|
|
} else {
|
|
// Legacy behavior:
|
|
return (APP_MESSAGE_OUTBOX_SIZE_MINIMUM + APP_MSG_HDR_OVRHD_SIZE);
|
|
}
|
|
}
|
|
|
|
AppMessageResult app_message_open(const uint32_t size_inbound, const uint32_t size_outbound) {
|
|
// We're making this assumption in this file; here's as good a place to check it as any.
|
|
// It's probably not super-bad if this isn't true, but we'll have type casts between different
|
|
// sizes without over/underflow verification.
|
|
#ifndef UNITTEST
|
|
_Static_assert(sizeof(size_t) == sizeof(uint32_t), "sizeof(size_t) != sizeof(uint32_t)");
|
|
#endif
|
|
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
if (app_message_ctx->outbox.phase != OUT_CLOSED ||
|
|
app_message_ctx->inbox.is_open) {
|
|
return APP_MSG_INVALID_STATE; // Already open
|
|
}
|
|
|
|
AppMessageResult result = app_message_outbox_open(&app_message_ctx->outbox, size_outbound);
|
|
if (APP_MSG_OK != result) {
|
|
return result;
|
|
}
|
|
|
|
result = app_message_inbox_open(&app_message_ctx->inbox, size_inbound);
|
|
if (APP_MSG_OK != result) {
|
|
app_message_outbox_close(&app_message_ctx->outbox);
|
|
return result;
|
|
}
|
|
|
|
return APP_MSG_OK;
|
|
}
|
|
|
|
void app_message_close(void) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
|
|
// TODO PBL-1634: handle the return status when this function returns status.
|
|
// For now, continue to ignore failure.
|
|
app_message_outbox_close(&app_message_ctx->outbox);
|
|
app_message_inbox_close(&app_message_ctx->inbox);
|
|
|
|
app_message_deregister_callbacks();
|
|
}
|
|
|
|
// -------- Testing Interface (only) ------------------------------------------------------------ //
|
|
|
|
AppTimer *app_message_ack_timer_id(void) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
return app_message_ctx->outbox.ack_nack_timer;
|
|
}
|
|
|
|
bool app_message_is_accepting_inbound(void) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
return app_message_ctx->inbox.is_open;
|
|
}
|
|
|
|
bool app_message_is_accepting_outbound(void) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
return (app_message_ctx->outbox.phase == OUT_ACCEPTING);
|
|
}
|
|
|
|
bool app_message_is_closed_inbound(void) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
return (!app_message_ctx->inbox.is_open);
|
|
}
|
|
|
|
bool app_message_is_closed_outbound(void) {
|
|
AppMessageCtx *app_message_ctx = app_state_get_app_message_ctx();
|
|
return (app_message_ctx->outbox.phase == OUT_CLOSED);
|
|
}
|