Hi,

I have implemented voice call history plugin using memory mapped file , the implementation tested on meego images right now as an external plugin to ofono. I am submitting the code here so that it works as builtin plugin for ofono in future. Please review the patch and let me know if your decision.

Thanks
Raji Bommaraju
>From df8e20421beae9d0e988f506c750b5351466806f Mon Sep 17 00:00:00 2001
From: Raji Bommaraju <Rajyalakshmi Bommaraju>
Date: Mon, 17 May 2010 11:08:58 -0700
Subject: [PATCH] Merging callhistory plugin implementation

---
 Makefile.am           |    3 +
 plugins/callhistory.c |  579 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 582 insertions(+), 0 deletions(-)
 create mode 100644 plugins/callhistory.c

diff --git a/Makefile.am b/Makefile.am
index 7fd862f..71d2440 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -239,6 +239,9 @@ builtin_sources += plugins/palmpre.c
 builtin_modules += ste
 builtin_sources += plugins/ste.c
 
+builtin_modules += callhistory 
+builtin_sources += plugins/callhistory.c
+
 endif
 
 if MAINTAINER_MODE
diff --git a/plugins/callhistory.c b/plugins/callhistory.c
new file mode 100644
index 0000000..3dce3c2
--- /dev/null
+++ b/plugins/callhistory.c
@@ -0,0 +1,579 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2010  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <glib.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/dir.h>
+#include <sys/mman.h>
+#include <semaphore.h>
+#include <fcntl.h>
+#include <ofono/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <ofono/types.h>
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/history.h>
+#include "gdbus.h"
+#include "common.h"
+
+#define HISTORY_FILE_PATH "/var/cache/callhistory/"
+#define HISTORY_FILE HISTORY_FILE_PATH"voicecallhistorydata"
+#define OFONO_MANAGER_PATH "/"
+#define PHONE_NUMBER_SIZE 64
+#define RECORD_SIZE 80
+#define HEADER_SIZE 16 
+
+#ifdef NUM_VCALL_HISTORY_ENTRIES
+#define MAX_ITEMS NUM_VCALL_HISTORY_ENTRIES
+#else
+#define MAX_ITEMS 50
+#endif
+
+#define TOTAL_SIZE (MAX_ITEMS * RECORD_SIZE + HEADER_SIZE)
+
+enum VOICE_CALL_TYPE {
+	OUTGOING = 0,
+	INCOMING,
+	MISSED
+};
+
+typedef struct _FILEHEADER {
+	int head;
+	int tail;
+	int unread;
+	unsigned int lastid; 
+} HistoryFileHeader;
+
+typedef struct _VOICEHISTORY {
+	int id;
+	char lineid[PHONE_NUMBER_SIZE];
+	short int calltype;
+	time_t starttime;
+	time_t endtime;
+} VoiceHistory;
+
+// Global Shared data
+typedef struct _SHARED
+{
+	HistoryFileHeader header;
+	void *dataMap;
+	sem_t mutex;
+	int temp_unread;
+	int temp_tail;
+} SharedData;
+
+static SharedData *shared_data = NULL;
+
+
+static void callhistory_emit_voice_history_changed(int );
+
+static int call_history_probe(struct ofono_history_context *context)
+{
+	DBG("History Probe for modem: %p", context->modem);
+	return 0;
+}
+
+static void call_history_remove(struct ofono_history_context *context)
+{
+	DBG("Example History Remove for modem: %p", context->modem);
+}
+
+static void clean_up()
+{
+	g_return_if_fail(shared_data!= NULL);
+
+	sem_wait(&(shared_data->mutex));
+	// unmap
+	munmap(shared_data->dataMap,TOTAL_SIZE);
+	sem_post(&(shared_data->mutex));
+
+	// remove semaphore
+	if (sem_destroy(&(shared_data->mutex)) < 0)
+		perror("sem_destroy failed");
+
+	g_free(shared_data);
+}
+
+
+static int init_sem()
+{
+	unsigned int value = 1;
+	g_return_val_if_fail(shared_data!= NULL,-1);
+
+	if (sem_init(&(shared_data->mutex), TRUE, value) < 0){
+		perror("sem_init failed");
+		return -1;
+	}
+	return 0;
+}
+
+static int init_header(void *dataPtr)
+{
+	int unread = 0;
+
+	g_return_val_if_fail((shared_data!=NULL), -1);
+	g_return_val_if_fail((dataPtr!=NULL), -1);
+	unread = (shared_data->header).unread;
+
+	sem_wait(&(shared_data->mutex));
+	memcpy(&(shared_data->header), (HistoryFileHeader *)(dataPtr),
+							HEADER_SIZE);
+
+	ofono_debug("Unread: %d, Header: %d, Tail: %d, serialno: %d\n",
+					unread,
+					(shared_data->header).head,
+					(shared_data->header).tail,
+					(shared_data->header).lastid);
+	sem_post(&(shared_data->mutex));
+	if (unread > 0){
+		callhistory_emit_voice_history_changed(unread);
+	}
+	return 0;
+}
+
+/**
+ * Creates file  "callhistory"
+ */
+static gboolean init_file()
+{
+	int historyFile;
+	struct stat statbuf;
+	DIR *dirptr;
+	char *fill = NULL;
+
+	g_return_val_if_fail((shared_data!=NULL), FALSE);
+
+	if (!(dirptr = opendir(HISTORY_FILE_PATH)) ){
+		ofono_debug("%s: doesnt exist",HISTORY_FILE_PATH);
+		if (mkdir(HISTORY_FILE_PATH,
+				S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0){
+			perror("Error creating callhistory dir");
+			return FALSE;
+		}
+	}
+
+	if ((historyFile = open( HISTORY_FILE,
+				O_RDWR|O_CREAT|O_APPEND,
+				S_IRWXU))
+				< 0){
+		perror("Open file failed");
+		return FALSE;
+	}
+
+	if (stat( HISTORY_FILE,&statbuf) < -1){
+		perror("stat failed");
+		return FALSE;
+	}
+	ofono_debug("stat file: %d",(int) (statbuf.st_size));
+
+	if (statbuf.st_size == 0){
+		int byteswritten = 0;
+		//write header to the file
+		(shared_data->header).head=HEADER_SIZE;
+		(shared_data->header).tail=(shared_data->header).head;
+		(shared_data->header).unread=0;
+		ofono_debug("setting head: %d,tail: %d,unread: %d",
+						(shared_data->header).head,
+						(shared_data->header).tail,
+						(shared_data->header).unread);
+
+		//  fill the file with zeros
+		ofono_debug("Trying to allocate %d size",TOTAL_SIZE);
+		fill = (char *) g_try_malloc0(TOTAL_SIZE);
+         	if (!fill ){
+             		ofono_debug("Error allocating init memory");
+             		return FALSE;
+		}
+        
+		// Predefine file size and fill with some data
+		bzero(fill,TOTAL_SIZE);
+		memcpy(fill,&(shared_data->header), HEADER_SIZE);
+
+		if ((byteswritten = write (historyFile,fill, TOTAL_SIZE)) <0){
+			ofono_debug("Error writing to file");
+			return FALSE;
+		}
+		g_free(fill);
+
+		statbuf.st_size = TOTAL_SIZE;
+		ofono_debug("History file %d size created \n",byteswritten);
+	}
+	// create semaphore
+	if (init_sem() < 0)
+		return FALSE; 
+
+	sem_wait(&(shared_data->mutex));
+	shared_data->dataMap = mmap(( caddr_t)NULL,
+				statbuf.st_size,
+				PROT_READ|PROT_WRITE|PROT_EXEC,
+				MAP_SHARED,
+				historyFile,
+				0);
+	sem_post(&(shared_data->mutex));
+	if (shared_data->dataMap == (void *)(-1)){
+		perror("mmap falied");
+		return FALSE;
+	}
+
+	close(historyFile);
+
+	init_header(shared_data->dataMap);
+
+	return TRUE;
+}
+
+static void sync_mem_file(VoiceHistory *vcall){
+	void *writeMap = NULL;
+	int unread = 0;
+
+	g_return_if_fail(vcall != NULL);
+	g_return_if_fail(shared_data != NULL);
+	g_return_if_fail(shared_data->dataMap != NULL);
+
+	sem_wait(&(shared_data->mutex));
+
+	writeMap = shared_data->dataMap;
+	writeMap = writeMap + (shared_data->header).head; 
+	vcall->id = (shared_data->header).lastid;
+	(shared_data->header).lastid++;
+	memcpy(writeMap, vcall, RECORD_SIZE);
+
+	// update header
+	((shared_data->header).unread)++;
+	(shared_data->header).head += RECORD_SIZE;
+	if ((shared_data->header).lastid >= UINT_MAX)
+		(shared_data->header).lastid = 0;
+
+	unread = (shared_data->header).unread;
+	
+	//header reaches end of file
+	if ((shared_data->header).head >= TOTAL_SIZE){
+		//reset head back to front
+		(shared_data->header).head = HEADER_SIZE;
+
+        	ofono_debug("Header = %d, Tail=%d",
+				(shared_data->header).head,
+				(shared_data->header).tail);
+
+        	if (unread >= MAX_ITEMS)
+           		ofono_error("No one reading history data");
+    	}
+
+	memcpy(shared_data->dataMap,&(shared_data->header),HEADER_SIZE);
+	msync(shared_data->dataMap, TOTAL_SIZE, MS_ASYNC);
+
+	sem_post(&(shared_data->mutex));
+
+	if (unread > 0)
+		callhistory_emit_voice_history_changed(unread);
+    	
+}
+
+/**
+ * call_history_call_ended:
+ * ofono calls this method with the call information
+ */
+ 
+static void call_history_call_ended(struct ofono_history_context *context,
+				const struct ofono_call *call,
+				time_t start,
+                                time_t end)
+{
+	const char *from = "Unknown";
+	struct tm tmstart, tmend;
+	char sttime[128], endtime[128];
+	int fromlen = 0;
+	VoiceHistory vcall;
+
+	ofono_debug("Call Ended on modem: %p", context->modem);
+
+	if (call->type != 0)
+		return;
+	ofono_debug("Voice Call, %s",
+		call->direction ? "Incoming" : "Outgoing");
+
+	if (call->direction)
+		vcall.calltype = INCOMING;
+	else
+		vcall.calltype = OUTGOING;
+
+	if (call->clip_validity == 0)
+		from = phone_number_to_string(&call->phone_number);
+	fromlen = strlen(from);
+	strcpy(vcall.lineid, from);
+	vcall.lineid[fromlen] = '\0';
+
+	vcall.starttime = start;
+	gmtime_r(&start, &tmstart);
+	strftime(sttime, 127, "%a, %d %b %Y %H:%M:%S %z", &tmstart);
+	sttime[127] = '\0';
+
+	ofono_debug("StartTime: %s",sttime);
+
+	vcall.endtime = end;
+	gmtime_r(&end, &tmend);
+	strftime(endtime, 127, "%a, %d %b %Y %H:%M:%S %z", &tmend);
+	endtime[127] = '\0';
+	ofono_debug("EndTime: %s",endtime);
+
+	sync_mem_file(&vcall);
+}
+
+/**
+ * call_history_call_missed:
+ * ofono calls this method with the call information
+ */
+static void call_history_call_missed(struct ofono_history_context *context,
+					const struct ofono_call *call,
+					time_t when)
+{
+	const char *from = "Unknown";
+	struct tm mtime;
+	char sttime[128];
+	VoiceHistory vcall;
+	int fromlen = 0;
+
+	ofono_debug("Call Missed on modem: %p", context->modem);
+
+	if (call->type != 0)
+		return;
+
+	ofono_debug("Voice Call, Missed");
+	ofono_debug("Voice Call, %s",
+		call->direction ? "Incoming" : "Outgoing");
+
+	vcall.calltype = MISSED;
+
+	if (call->clip_validity == 0)
+		from = phone_number_to_string(&call->phone_number);
+
+	fromlen = strlen(from);
+	strncpy(vcall.lineid, from ,fromlen);
+	vcall.lineid[fromlen] = '\0';
+
+	vcall.starttime = when;
+
+	gmtime_r(&when, &mtime);
+	ofono_debug("From: %s", vcall.lineid);
+	strftime(sttime, 127, "%a, %d %b %Y %H:%M:%S %z", &mtime);
+	sttime[127] = '\0';
+	ofono_debug("Missed Time: %s", sttime);
+        
+	vcall.endtime = when;
+
+	sync_mem_file(&vcall);
+}
+
+
+/* Start of DBus stuff */
+/* *************************************************************************
+ * Expose an interface, properties and signals for querying storage backend
+ * *************************************************************************/
+#define OFONO_CALL_HISTORY_INTERFACE OFONO_SERVICE".CallHistory"
+
+static void callhistory_emit_voice_history_changed(int userdata)
+{
+	int *valint = &userdata;
+	DBusConnection *conn = ofono_dbus_get_connection();
+	g_dbus_emit_signal(conn,
+		OFONO_MANAGER_PATH,
+		OFONO_CALL_HISTORY_INTERFACE,
+		"VoiceHistoryChanged",
+		DBUS_TYPE_UINT32,
+		valint,
+		DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *call_history_set_voice_history_read(DBusConnection *conn,
+							DBusMessage *msg,
+							void *userdata)
+{
+	DBusMessage *reply;
+	DBusMessageIter iter;
+	g_return_val_if_fail((shared_data!=NULL), NULL);
+
+	ofono_debug("Read ack received");
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return NULL;
+
+	dbus_message_iter_init_append(reply, &iter);
+	sem_wait(&(shared_data->mutex));
+	shared_data->header.unread = (shared_data->header).unread -
+					shared_data->temp_unread;
+	shared_data->header.tail = shared_data->temp_tail;
+ 
+	// write to file
+	memcpy(shared_data->dataMap,&(shared_data->header),HEADER_SIZE);
+	sem_post(&(shared_data->mutex));
+	msync(shared_data->dataMap,TOTAL_SIZE, MS_ASYNC);
+	return reply;
+}
+
+static void read_data(VoiceHistory *data,int temp)
+{
+	void *readMap = NULL;
+	g_return_if_fail(shared_data != NULL);
+	g_return_if_fail(shared_data->dataMap != NULL);
+
+	// lock 
+	sem_wait(&(shared_data->mutex));
+	readMap = shared_data->dataMap+temp;
+	// read a chunk into *data
+	memcpy(data, readMap,RECORD_SIZE);
+	shared_data->temp_unread++;
+	sem_post(&(shared_data->mutex));
+}
+
+static DBusMessage *call_history_get_voice_history(DBusConnection *conn,
+						DBusMessage *msg,
+						void *userdata)
+{
+	DBusMessage     *reply;
+	DBusMessageIter  iter;
+	DBusMessageIter  array,struct_s;
+	int i;
+	char *lineid;
+	VoiceHistory data;
+	int unread = 0;
+
+	reply = dbus_message_new_method_return(msg);
+
+	if (!reply)
+		return NULL;
+	g_return_val_if_fail(shared_data!=NULL,NULL);
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	dbus_message_iter_open_container(&iter,DBUS_TYPE_ARRAY,
+					DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+					DBUS_TYPE_UINT32_AS_STRING
+					DBUS_TYPE_STRING_AS_STRING
+					DBUS_TYPE_UINT16_AS_STRING
+					DBUS_TYPE_INT32_AS_STRING
+					DBUS_TYPE_INT32_AS_STRING
+					DBUS_STRUCT_END_CHAR_AS_STRING
+					,&array);
+
+	sem_wait(&(shared_data->mutex));
+	shared_data->temp_unread=0;
+	shared_data->temp_tail = (shared_data->header).tail;
+	unread = (shared_data->header).unread;
+	sem_post(&(shared_data->mutex));
+
+	for (i=0; i < unread; i++){
+		read_data(&data, shared_data->temp_tail);
+		lineid = data.lineid;
+		dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT,NULL,
+						&struct_s);
+		dbus_message_iter_append_basic(&struct_s, DBUS_TYPE_UINT32,
+						&(data.id));
+		dbus_message_iter_append_basic(&struct_s, DBUS_TYPE_STRING,
+						&(lineid));
+		dbus_message_iter_append_basic(&struct_s, DBUS_TYPE_UINT16,
+						&(data.calltype));
+		dbus_message_iter_append_basic(&struct_s, DBUS_TYPE_INT32,
+						&(data.starttime));
+		dbus_message_iter_append_basic(&struct_s, DBUS_TYPE_INT32,
+						&(data.endtime));
+		dbus_message_iter_close_container(&array, &struct_s);
+
+		shared_data->temp_tail = shared_data->temp_tail + RECORD_SIZE;
+
+		if (shared_data->temp_tail == TOTAL_SIZE){
+			ofono_debug ("End of Queue: %d",shared_data->temp_tail);		// reset back to front
+		shared_data->temp_tail = HEADER_SIZE;
+		}
+	}
+	dbus_message_iter_close_container(&iter,&array);
+
+	return reply;
+}
+
+static GDBusMethodTable call_history_methods[] = {
+	{ "GetVoiceHistory", "", "a(usqii)", call_history_get_voice_history },
+	{ "SetVoiceHistoryRead", "", "", call_history_set_voice_history_read },
+	{ }
+};
+
+static GDBusSignalTable call_history_signals[] = {
+	{ "VoiceHistoryChanged","u" },
+	{}
+};
+
+/* End of DBus stuff */
+static struct ofono_history_driver call_history_driver = {
+	.name = "callhistory",
+	.probe = call_history_probe,
+	.remove = call_history_remove,
+	.call_ended = call_history_call_ended,
+	.call_missed = call_history_call_missed,
+};
+
+static int call_history_init(void)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+
+	if (!shared_data)
+		if (!(shared_data = g_try_new0(SharedData, 1)))
+			return -ENOMEM;
+
+
+	if (!g_dbus_register_interface(conn,
+					OFONO_MANAGER_PATH,
+					OFONO_CALL_HISTORY_INTERFACE,
+					call_history_methods,
+					call_history_signals,
+					NULL,    /* Properties */
+					shared_data,    /* Userdata   */
+					NULL))    /* Destroy func */
+        return -EIO;
+
+    	if (!init_file())
+       		return -ENOENT;
+
+	return ofono_history_driver_register(&call_history_driver);
+}
+
+static void call_history_exit(void)
+{
+	clean_up();
+	ofono_history_driver_unregister(&call_history_driver);
+}
+
+OFONO_PLUGIN_DEFINE(callhistory, "Call History Plugin",
+			OFONO_VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
+			call_history_init, call_history_exit)
-- 
1.6.6.1

_______________________________________________
ofono mailing list
[email protected]
http://lists.ofono.org/listinfo/ofono

Reply via email to