mirror of https://github.com/google/pebble
220 lines
6.3 KiB
C
220 lines
6.3 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 "poll_remote.h"
|
|
#include "services/common/comm_session/protocol.h"
|
|
#include "services/common/comm_session/session.h"
|
|
#include "services/common/system_task.h"
|
|
#include "system/logging.h"
|
|
#include "system/passert.h"
|
|
#include "util/attributes.h"
|
|
|
|
#include <string.h>
|
|
|
|
/*
|
|
* Private
|
|
*/
|
|
|
|
static const uint8_t MIN_INTERVAL_MINUTES = 1;
|
|
static const uint16_t ENDPOINT_ID = 0xcafe;
|
|
static bool s_running = false;
|
|
|
|
typedef enum {
|
|
CMD_POLL = 0x0, // Formerly command poll mail
|
|
LEGACY_CMD_REQUEST_INTERVAL = 0x1, // for backwards compatibility
|
|
CMD_SET_INTERVAL = 0x2,
|
|
CMD_REQUEST_POLL = 0x3,
|
|
} PollRemoteCommand;
|
|
|
|
// Deprecated -- used to set the mail poll interval
|
|
typedef struct PACKED {
|
|
uint8_t cmd;
|
|
uint8_t interval_minutes;
|
|
} PollLegacySetIntervalMessage;
|
|
|
|
// Poll a service at a specific interval
|
|
typedef struct PACKED {
|
|
PollRemoteCommand cmd;
|
|
PollRemoteService service;
|
|
uint8_t interval_minutes;
|
|
} PollSetIntervalMessage;
|
|
|
|
// Request to poll a service now
|
|
typedef struct PACKED {
|
|
PollRemoteCommand cmd;
|
|
PollRemoteService service;
|
|
} PollRequestMessage;
|
|
|
|
typedef struct PACKED {
|
|
PollRemoteCommand cmd;
|
|
PollRemoteService service;
|
|
} PollRemoteMessage;
|
|
|
|
//! Struct that holds all the state required for a remote polling subsystem.
|
|
typedef struct {
|
|
PollRemoteService service;
|
|
//! The minimum interval between two "poll services" requests.
|
|
//! Calls to poll_remote_send_request() will be no-ops if min_interval_minutes has not been reached.
|
|
uint8_t min_interval_minutes;
|
|
|
|
//! The maximum interval between two "poll services" requests.
|
|
//! The automatic sending of poll requests will only occur when max_interval_minutes is reached.
|
|
uint8_t max_interval_minutes;
|
|
|
|
uint8_t counted_minutes; //!< Number of minutes passed since the last request
|
|
} PollRemoteContext;
|
|
|
|
static void poll_service_timer_callback();
|
|
|
|
static RegularTimerInfo s_poll_timer = {
|
|
.cb = poll_service_timer_callback,
|
|
.cb_data = NULL,
|
|
};
|
|
|
|
static PollRemoteContext s_poll_remote_contexts[NUM_POLL_REMOTE_SERVICES];
|
|
|
|
static void for_each_context(void (*poll_remote_function)(PollRemoteContext *ctx)) {
|
|
for (int i = 0; i < NUM_POLL_REMOTE_SERVICES; i++) {
|
|
poll_remote_function(&s_poll_remote_contexts[i]);
|
|
}
|
|
}
|
|
|
|
static bool has_min_interval_passed(PollRemoteContext *ctx) {
|
|
return (ctx->counted_minutes >= ctx->min_interval_minutes);
|
|
}
|
|
|
|
static bool has_max_interval_passed(PollRemoteContext *ctx) {
|
|
return (ctx->counted_minutes >= ctx->max_interval_minutes);
|
|
}
|
|
|
|
// SystemTaskCallback
|
|
static void prv_send_request(PollRemoteContext *ctx) {
|
|
if (has_min_interval_passed(ctx) == false) {
|
|
return;
|
|
}
|
|
CommSession *session = comm_session_get_system_session();
|
|
if (!session) {
|
|
return;
|
|
}
|
|
// [MT]: comm_session_send_data() doesn't make the link active,
|
|
// which is what we want here. If this this changes in the future
|
|
// we need to take measures here to make sure we don't pull the link active.
|
|
const PollRemoteMessage msg = {
|
|
.cmd = CMD_POLL,
|
|
.service = ctx->service
|
|
};
|
|
comm_session_send_data(session, ENDPOINT_ID, (const uint8_t *)&msg, sizeof(PollRemoteMessage),
|
|
COMM_SESSION_DEFAULT_TIMEOUT);
|
|
ctx->counted_minutes = 0;
|
|
}
|
|
|
|
static void context_interval_check(PollRemoteContext *ctx) {
|
|
if (ctx->max_interval_minutes == 0) {
|
|
return;
|
|
}
|
|
|
|
ctx->counted_minutes++;
|
|
|
|
if (has_max_interval_passed(ctx)) {
|
|
prv_send_request(ctx);
|
|
}
|
|
}
|
|
|
|
|
|
static void start(PollRemoteContext *ctx) {
|
|
ctx->counted_minutes = 0;
|
|
}
|
|
|
|
static void set_intervals(PollRemoteContext *ctx, const uint8_t min_interval_minutes, const uint8_t max_interval_minutes) {
|
|
ctx->min_interval_minutes = min_interval_minutes;
|
|
ctx->max_interval_minutes = max_interval_minutes;
|
|
}
|
|
|
|
void comm_poll_remote_protocol_msg_callback(CommSession *session, const uint8_t* data, size_t length) {
|
|
PollRemoteCommand cmd = data[0];
|
|
|
|
switch (cmd) {
|
|
case CMD_REQUEST_POLL: {
|
|
PollRequestMessage *msg = (PollRequestMessage *)data;
|
|
if (msg->service >= NUM_POLL_REMOTE_SERVICES) { return; }
|
|
prv_send_request(&s_poll_remote_contexts[msg->service]);
|
|
break;
|
|
}
|
|
case LEGACY_CMD_REQUEST_INTERVAL: {
|
|
PollLegacySetIntervalMessage *msg = (PollLegacySetIntervalMessage *)data;
|
|
poll_remote_set_intervals(POLL_REMOTE_SERVICE_MAIL, MIN_INTERVAL_MINUTES, msg->interval_minutes);
|
|
break;
|
|
}
|
|
case CMD_SET_INTERVAL: {
|
|
PollSetIntervalMessage *msg = (PollSetIntervalMessage *)data;
|
|
if (msg->service >= NUM_POLL_REMOTE_SERVICES) { return; }
|
|
poll_remote_set_intervals(msg->service, MIN_INTERVAL_MINUTES, msg->interval_minutes);
|
|
break;
|
|
}
|
|
default: {
|
|
PBL_LOG(LOG_LEVEL_ERROR, "Invalid command.");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Public
|
|
*/
|
|
|
|
static void poll_service_system_task_callback(void *data) {
|
|
PBL_ASSERTN(s_running);
|
|
for_each_context(context_interval_check);
|
|
}
|
|
|
|
static void poll_service_timer_callback(void *data) {
|
|
system_task_add_callback(poll_service_system_task_callback, data);
|
|
}
|
|
|
|
void poll_remote_init(void) {
|
|
for (int i = 0; i < NUM_POLL_REMOTE_SERVICES; i++) {
|
|
s_poll_remote_contexts[i].service = i;
|
|
}
|
|
}
|
|
|
|
void poll_remote_send_request(PollRemoteService service) {
|
|
prv_send_request(&s_poll_remote_contexts[service]);
|
|
}
|
|
|
|
void poll_remote_start(void) {
|
|
if (s_running) {
|
|
return;
|
|
}
|
|
|
|
s_running = true;
|
|
for_each_context(start);
|
|
regular_timer_add_minutes_callback(&s_poll_timer);
|
|
}
|
|
|
|
void poll_remote_stop(void) {
|
|
if (!s_running) {
|
|
return;
|
|
}
|
|
|
|
s_running = false;
|
|
regular_timer_remove_callback(&s_poll_timer);
|
|
}
|
|
|
|
void poll_remote_set_intervals(PollRemoteService service, const uint8_t min_interval_minutes, const uint8_t max_interval_minutes) {
|
|
set_intervals(&s_poll_remote_contexts[service], min_interval_minutes, max_interval_minutes);
|
|
(max_interval_minutes == 0) ? poll_remote_stop() : poll_remote_start();
|
|
}
|