pebble/src/fw/applib/plugin_service.c
2025-01-27 11:38:16 -08:00

159 lines
5.8 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 "event_service_client.h"
#include "applib/applib_malloc.auto.h"
#include "plugin_service.h"
#include "plugin_service_private.h"
#include "syscall/syscall.h"
#include "process_state/app_state/app_state.h"
#include "process_state/worker_state/worker_state.h"
#include "system/logging.h"
#include "system/passert.h"
// ---------------------------------------------------------------------------------------------------------------
// Get our state variables
static PluginServiceState* prv_get_state(PebbleTask task) {
if (task == PebbleTask_Unknown) {
task = pebble_task_get_current();
}
if (task == PebbleTask_App) {
return app_state_get_plugin_service();
} else {
PBL_ASSERTN(task == PebbleTask_Worker);
return worker_state_get_plugin_service();
}
}
// ---------------------------------------------------------------------------------------------------------------
// Lookup the plugin service index from the UUID. We store the index in the event structure instead of the UUID
// so that we have payload room.
static uint16_t prv_get_service_index(Uuid *uuid) {
return sys_event_service_get_plugin_service_index(uuid);
}
// ---------------------------------------------------------------------------------------------------------------
// Used by list_find to locate the handler for a specfic service index.
static bool prv_service_filter(ListNode *node, void *tp) {
PluginServiceEntry *info = (PluginServiceEntry *)node;
uint16_t service_idx = (uint16_t)(uintptr_t)tp;
return (info->service_index == service_idx);
}
// ---------------------------------------------------------------------------------------------------------------
// Callback provided to the app_event_service. All events of type PEBBLE_PLUGIN_SERVICE_EVENT that get sent to
// this task trigger this callback. From here, we look up which user-supplied callback corresponds to the
// service index stored in the event structure and then pass control to that user-supplied callback.
static void prv_handle_event_service_event(PebbleEvent *e, void *context) {
PluginServiceState *state = prv_get_state(PebbleTask_Unknown);
uint16_t service_index = e->plugin_service.service_index;
ListNode *found;
ListNode *list = &state->subscribed_services;
found = list_find(list, prv_service_filter, (void*)(uintptr_t)service_index);
if (!found) {
return;
}
// Call the handler provided by the client
PluginServiceEntry *entry = (PluginServiceEntry *)found;
entry->handler(e->plugin_service.type, &e->plugin_service.data);
}
// ---------------------------------------------------------------------------------------------------------------
// Subscribe to a specific plug-in service by uuid.
bool plugin_service_subscribe(Uuid *uuid, PluginServiceHandler handler) {
PluginServiceState *state = prv_get_state(PebbleTask_Unknown);
uint16_t service_index = prv_get_service_index(uuid);
ListNode *list = &state->subscribed_services;
if (list_find(list, prv_service_filter, (void*)(uintptr_t)service_index)) {
PBL_LOG(LOG_LEVEL_DEBUG, "Plug service handler already subscribed");
return false;
}
// Add to handlers list
PluginServiceEntry *entry = applib_type_zalloc(PluginServiceEntry);
if (!entry) {
PBL_LOG(LOG_LEVEL_ERROR, "OOM in plugin_service_subscribe");
return false;
}
entry->service_index = service_index;
entry->handler = handler;
list_append(list, &entry->list_node);
// Subscribe to the app event service if we haven't already
if (!state->subscribed_to_app_event_service) {
state->subscribed_to_app_event_service = true;
event_service_client_subscribe(&state->event_service_info);
}
return true;
}
// ---------------------------------------------------------------------------------------------------------------
// Unsubscribe from a specific plug-in service by uuid.
bool plugin_service_unsubscribe(Uuid *uuid) {
PluginServiceState *state = prv_get_state(PebbleTask_Unknown);
uint16_t service_index = prv_get_service_index(uuid);
ListNode *found;
ListNode *list = &state->subscribed_services;
found = list_find(list, prv_service_filter, (void*)(uintptr_t)service_index);
if (!found) {
PBL_LOG(LOG_LEVEL_DEBUG, "Plug service handler already unsubscribed");
return true;
}
list_remove(found, NULL, NULL);
applib_free(found);
return true;
}
// ---------------------------------------------------------------------------------------------------------------
// Send an event to all registered subscribers of the given plugin service identified by UUID.
void plugin_service_send_event(Uuid *uuid, uint8_t type, PluginEventData *data) {
uint16_t service_index = prv_get_service_index(uuid);
PebbleEvent event = {
.type = PEBBLE_PLUGIN_SERVICE_EVENT,
.plugin_service = {
.service_index = service_index,
.type = type,
.data = *data
},
};
sys_send_pebble_event_to_kernel(&event);
}
// ---------------------------------------------------------------------------------------------------------------
// Init our state variables.
void plugin_service_state_init(PluginServiceState *state) {
*state = (PluginServiceState) {
.event_service_info = {
.type = PEBBLE_PLUGIN_SERVICE_EVENT,
.handler = &prv_handle_event_service_event,
},
};
}