Alarm / src /

data.c

/*
 * Copyright (c) 2016 Samsung Electronics Co., Ltd
 *
 * Licensed under the Flora License, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://floralicense.org/license/
 *
 * 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 <efl_extension.h>
#include <app_alarm.h>

#include "$(appName).h"
#include "data.h"

static struct data_info {
	int ontime_alarm_id;		/* id of the alarm set at date */
	int recurring_alarm_id;		/* id of the delayed recurring alarm */
	int recurring_alarm_count;	/* recurring alarm invocations count */
	time_t t_ontime_alarm;
	data_alarm_scheduled_t scheduled_alarm_callback;
	data_alarm_callback_t ontime_alarm_callback;
	data_alarm_callback_t recurring_alarm_callback;
} s_info = {
	.ontime_alarm_id = -1,
	.recurring_alarm_id = -1,
	.recurring_alarm_count = -1,
	.t_ontime_alarm = 0,
	.scheduled_alarm_callback = NULL,
	.ontime_alarm_callback = NULL,
	.recurring_alarm_callback = NULL,
};

#define APP_CONTROL_OPERATION_ALARM_RECURRING "http://tizen.org/appcontrol/operation/my_recurring_alarm"
#define APP_CONTROL_OPERATION_ALARM_ONTIME "http://tizen.org/appcontrol/operation/my_ontime_alarm"
#define RECURRING_ALARMS_TO_BE_INVOKED 5
#define ALARM_DELAY 10
#define ALARMS_INTERVAL ALARM_STANDARD_INTERVAL_FIFTEEN_MINUTES
#define TIME_STRING_FORMAT_BUFFER_SIZE 10

static void _initialize_recurring_alarm(void);
static void _initialize_ontime_alarm(void);
static void _data_recurring_alarm_invoked(app_control_h app_control);
static void _data_ontime_alarm_invoked(app_control_h app_control);

/**
 * @brief Initialization function for data module.
 */
void data_initialize(void)
{
	time_t t_alarm;

	/*
	 * initialization of recurring alarm.
	 */
	_initialize_recurring_alarm();

	/*
	 * initialization of on-time alarm.
	 */
	_initialize_ontime_alarm();

	if (s_info.scheduled_alarm_callback) {
		t_alarm = time(NULL) + ALARM_DELAY;
		s_info.scheduled_alarm_callback(t_alarm);
	}
}

/**
 * @brief Finalization function for data module.
 */
void data_finalize(void)
{
	dlog_print(DLOG_DEBUG, LOG_TAG, "data finalize");

	if (s_info.recurring_alarm_id >= 0)
		alarm_cancel(s_info.recurring_alarm_id);

	if (s_info.ontime_alarm_id >= 0)
		alarm_cancel(s_info.ontime_alarm_id);
}

static bool _app_control_operation_equals(const char *predefined_operation, const char *app_control_operation)
{
	if (!predefined_operation && !app_control_operation)
		return true;

	if (!predefined_operation || !app_control_operation)
			return false;

	/*now we have non null strings*/
	if (strlen(predefined_operation) != strlen(app_control_operation))
		return false;

	return !strncmp(predefined_operation, app_control_operation, strlen(app_control_operation));
}

/* function is invoked by controller module (main.c)
 * to to handle app_controls call-backs from application life cycle loop
 */
void data_handle_app_control(app_control_h app_control)
{
	char *operation = NULL;
	int ret;

	dlog_print(DLOG_DEBUG, LOG_TAG, "data_handle_app_control");

	/*
	 * Get operation name bind to the app_control handle.
	 * If this function fails, then it is not possible to handle app_control
	 * call.
	 */
	ret = app_control_get_operation(app_control, &operation);
	if (ret != APP_CONTROL_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "Function app_control_get_operation() failed.");
		return;
	}
	dlog_print(DLOG_DEBUG, LOG_TAG, "operation: %s", operation);
	/*
	 * Based on app_control operation, then proper action is executed.
	 * In this case, only alarm related operations are supported. Below
	 * conditional statements verify if operation type match any of customly
	 * predefined alarm operation.
	 */
	if (_app_control_operation_equals(APP_CONTROL_OPERATION_ALARM_RECURRING, operation))
		_data_recurring_alarm_invoked(app_control);
	else if (_app_control_operation_equals(APP_CONTROL_OPERATION_ALARM_ONTIME, operation))
		_data_ontime_alarm_invoked(app_control);

	free(operation);
}

/**
 * Function sets callbacks that are invoked when recurring and ontime alarms are fired
 */
void data_set_alarms_callbacks(data_alarm_scheduled_t scheduled_alarm_callback, data_alarm_callback_t ontime_alarm_callback, data_alarm_callback_t recurring_alarm_callback)
{
	s_info.ontime_alarm_callback = ontime_alarm_callback;
	s_info.recurring_alarm_callback = recurring_alarm_callback;
	s_info.scheduled_alarm_callback = scheduled_alarm_callback;
}

/*
 * Function returns current local time in string format: HH:MM:SS
 * Obtained char buffer must be released using free() function.
 */
static char* _get_current_time(void)
{
	char *time_buff = (char *)calloc(TIME_STRING_FORMAT_BUFFER_SIZE, sizeof(char));
	time_t current_time;

	time(&current_time);
	strftime(time_buff, TIME_STRING_FORMAT_BUFFER_SIZE, "%H:%M:%S", localtime(&current_time));

	return time_buff;
}

/*
 * Function creates new recurring alarm using alarm_schedule_after_delay()
 * function. If the alarm is created successfully, then it will be fired
 * with delay of ALARM_DELAY seconds and with interval of ALARMS_INTERVAL
 * seconds.
 */
static void _initialize_recurring_alarm(void)
{
	int ret;
	app_control_h app_control;

	/*
	 * Creating new app_control handle.
	 * If this function fails, the alarm will not be scheduled.
	 */
	ret = app_control_create(&app_control);
	if (ret != APP_CONTROL_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "Function app_control_create() failed.");
		return;
	}

	/*
	 * Bind operation to created app_control handle.
	 * Custom operation name will be used to identify the alarm invocation
	 * within app_control callback and distinguish alarm types.
	 * If this function fails, the app_control handle must be released
	 * and the alarm will not be scheduled.
	 */
	ret = app_control_set_operation(app_control, APP_CONTROL_OPERATION_ALARM_RECURRING);
	if (ret != APP_CONTROL_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "Function app_control_set_operation() failed.");
		app_control_destroy(app_control);
		return;
	}

	/*
	 * Bind package name to created app_control handle.
	 * Based on provided package name, the alarm will be invoked within
	 * the context of the application referenced by provided package name.
	 * If this function fails, the app_control handle must be released
	 * and the alarm will not be scheduled.
	 */
	ret = app_control_set_app_id(app_control, PACKAGE);
	if (ret != APP_CONTROL_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "Function app_control_set_app_id() failed.");
		app_control_destroy(app_control);
		return;
	}

	/*
	 * Set the alarm for created app_control handle.
	 * The alarm will be scheduled with ALARM_DELAY delay and ALARMS_INTERVAL
	 * period. In result, the first alarm invokation will occur ofter
	 * ALARM_DELAY seconds. Subsequent alarms invokations will occur
	 * with ALARMS_INTERVAL seconds. If ALARMS_INTERVAL == 0, then the alarm
	 * will be invoked only once after ALARM_DELAY seconds.
	 * If function succeeds, the alarm ID is returned and stored in appdata_s
	 * structure.
	 */
	ret = alarm_schedule_after_delay(app_control, ALARM_DELAY, ALARMS_INTERVAL, &s_info.recurring_alarm_id);
	if (ret != ALARM_ERROR_NONE)
		dlog_print(DLOG_ERROR, LOG_TAG, "Function alarm_schedule_after_delay() failed.");

	/*
	 * Finally, the app_control handle is released.
	 */
	ret = app_control_destroy(app_control);
	if (ret != APP_CONTROL_ERROR_NONE)
		dlog_print(DLOG_ERROR, LOG_TAG, "Function app_control_destroy() failed.");
	else
		dlog_print(DLOG_DEBUG, LOG_TAG, "Set recurring alarm with id: %i", s_info.recurring_alarm_id);
}

/*
 * Function creates new alarm set on time, using alarm_schedule_at_date()
 * function. If the alarm is created successfully, then it will be fired
 * at specified date and time.
 */
static void _initialize_ontime_alarm(void)
{
	int ret;
	app_control_h app_control;

	/*
	 * Creating new app_control handle.
	 * If this function fails, the alarm will not be scheduled.
	 */
	ret = app_control_create(&app_control);
	if (ret != 0) {
		dlog_print(DLOG_ERROR, LOG_TAG, "Function app_control_create() failed.");
		return;
	}

	/*
	 * Bind operation to created app_control handle.
	 * Custom operation name will be used to identify the alarm invokation
	 * within app_control callback and distinguish alarm types.
	 * If this function fails, the app_control handle must be released
	 * and the alarm will not be scheduled.
	 */
	ret = app_control_set_operation(app_control, APP_CONTROL_OPERATION_ALARM_ONTIME);
	if (ret != APP_CONTROL_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "Function app_control_set_operation() failed.");
		app_control_destroy(app_control);
		return;
	}

	/*
	 * Bind package name to created app_control handle.
	 * Based on provided package name, the alarm will be invoked within
	 * the context of the application referenced by provided package name.
	 * If this function fails, the app_control handle must be released
	 * and the alarm will not be scheduled.
	 */
	ret = app_control_set_app_id(app_control, PACKAGE);
	if (ret != APP_CONTROL_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "Function app_control_set_app_id() failed.");
		app_control_destroy(app_control);
		return;
	}

	/*
	 * Compute the time of on-time alarm invocation.
	 */
	s_info.t_ontime_alarm = time(NULL) + ALARM_DELAY;

	/*
	 * Set the alarm for created app_control handle.
	 * The alarm will be scheduled on time t_alarm without recurrency.
	 * If function succeeds, the alarm ID is returned and stored in appdata_s
	 * structure.
	 */
	ret = alarm_schedule_once_at_date(app_control, localtime(&s_info.t_ontime_alarm), &s_info.ontime_alarm_id);
	if (ret != ALARM_ERROR_NONE)
		dlog_print(DLOG_ERROR, LOG_TAG, "Function alarm_schedule_at_date() failed.");

	/*
	 * Finally, the app_control handle is released.
	 */
	ret = app_control_destroy(app_control);
	if (ret != APP_CONTROL_ERROR_NONE)
		dlog_print(DLOG_ERROR, LOG_TAG, "Function app_control_destroy() failed.");
	else
		dlog_print(DLOG_DEBUG, LOG_TAG, "Set ontime alarm with id: %i", s_info.ontime_alarm_id);
}

/*
 * Function is called if alarm control arrives and is identified
 * as recurring alarm.
 */
static void _data_recurring_alarm_invoked(app_control_h app_control)
{
	int ret;
	char *alarm_data = NULL;
	char *time_str = NULL;
	time_t t_alarm;

	dlog_print(DLOG_DEBUG, LOG_TAG, "recurring alarm invoked by appcontrol");

	if (s_info.scheduled_alarm_callback) {
		t_alarm = time(NULL) + ALARMS_INTERVAL;
		s_info.scheduled_alarm_callback(t_alarm);
	}

	/*
	 * Get data attached to the app_control handle by using common alarm data
	 * identifier APP_CONTROL_DATA_ALARM_ID. The extracted data contains alarm
	 * identifier stored as char buffer.
	 * If this function fails, the alarm can not be properly verified.
	 */
	ret = app_control_get_extra_data(app_control, APP_CONTROL_DATA_ALARM_ID, &alarm_data);

	if (ret != APP_CONTROL_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "Function app_control_get_extra_data() failed.");
		return;
	}

	if (alarm_data == NULL) {
		dlog_print(DLOG_ERROR, LOG_TAG, "alarm_data == NULL");
		return;
	}
	/*
	 * Verify if obtained alarm identifier is equal to the identifier
	 * of created alarm. If IDs does not match, then the alarm invocation
	 * belongs to other recurring alarm.
	 */
	if (atoi(alarm_data) != s_info.recurring_alarm_id)
		return;

	/*
	 * Count number of recurring alarm invocations
	 */
	s_info.recurring_alarm_count++;

	/*
	 * Inform controller about firing an alarm.
	 */
	if (s_info.recurring_alarm_callback)
		s_info.recurring_alarm_callback();

	time_str = _get_current_time();
	dlog_print(DLOG_INFO, LOG_TAG, "Recurring alarm #%d invoked at %s", s_info.recurring_alarm_count, time_str);
	free(time_str);

	/*
	 * If number of recurring alarm invocations is less then expected number,
	 * then the function ends its execution and the alarm will be invoked again.
	 */
	if (s_info.recurring_alarm_count < RECURRING_ALARMS_TO_BE_INVOKED)
		return;

	/*
	 * If recurring alarm is invoked expected number of times, then it is
	 * canceled. If this function fails, then the recurring alarm will not
	 * be stopped.
	 */
	ret = alarm_cancel(s_info.recurring_alarm_id);
	if (ret != APP_CONTROL_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "Function alarm_cancel() failed.");
		return;
	}

	dlog_print(DLOG_INFO, LOG_TAG, "Recurring alarm canceled");

	if (s_info.scheduled_alarm_callback)
		s_info.scheduled_alarm_callback(s_info.t_ontime_alarm);
}

/*
 * Function is called if alarm control arrives and is identified as on-time alarm.
 */
static void _data_ontime_alarm_invoked(app_control_h app_control)
{
	int ret;
	char *alarm_data = NULL;
	char *time_str = NULL;

	dlog_print(DLOG_DEBUG, LOG_TAG, "ontime alarm invoked by appcontrol");

	if (s_info.scheduled_alarm_callback)
		s_info.scheduled_alarm_callback(0);

	/*
	 * Get data attached to the app_control handle by using common alarm data
	 * identifier APP_CONTROL_DATA_ALARM_ID. The extracted data contains alarm
	 * identifier stored as char buffer.
	 * If this function fails, the alarm can not be properly verified.
	 */
	ret = app_control_get_extra_data(app_control, APP_CONTROL_DATA_ALARM_ID, &alarm_data);
	if (ret != APP_CONTROL_ERROR_NONE) {
		dlog_print(DLOG_ERROR, LOG_TAG, "Function app_control_get_extra_data() failed.");
		return;
	}

	if (alarm_data == NULL) {
		dlog_print(DLOG_ERROR, LOG_TAG, "alarm_data empty");
		return;
	}

	/*
	 * Verify if obtained alarm identifier is equal to the identifier of
	 * created alarm. If IDs does not match, then the alarm invocation belongs
	 * to other on-time alarm.
	 */
	if (atoi(alarm_data) != s_info.ontime_alarm_id)
		return;

	/*
	 * Inform controller about firing an alarm.
	 */
	if (s_info.ontime_alarm_callback)
		s_info.ontime_alarm_callback();

	time_str = _get_current_time();
	dlog_print(DLOG_INFO, LOG_TAG, "Ontime alarm invoked at %s", time_str);
	free(time_str);

	/*
	 * If the alarm is not set as recurring, then it must not call
	 * alarm_cancel() function. In this case, the on-tim alarm is automatically
	 * released.
	 */
}