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();
 | |
| }
 |