A friend of mine recently dropped her Android phone on the floor and the screen cranked - only the backlight would come on, but nothing was displayed. As she didn't sync her contacts, she asked me how to get her contacts back. Here is what I did:
I remembered that I once installed ClockWorkMod recovery alongside with CyanogenMod on the device. Unfortunately, I left USB debugging disabled in Android, as I had hoped to be able to pull the database files via ADB on plugging the phone in. Luckily, CWM comes with ADB debugging enabled, so I booted straight into CWM (on the ZTE Blade by holding down the Vol- button while powering the phone on), attached the phone to the computer and logged in. First step: Mount the data partition with
andy@x200t:~$ adb shell android:~$ mount /data android:~$ exit
Then, pull the contacts file:
andy@x200t:~$ adb pull /data/data/com.android.providers.contacts/contacts2.db
This is actually a SQLite database containing all the contact's information. A short research on the internet revealed nothing of interest, so I had to dig myself into the format of the file. I came up with a simple Python converter script, that converts most of the information into a VCard file. Phone numbers are all trated as cell numbers and E-Mail addresses are all treated as home mail addresses. I put question marks on the fields where I suspect lies the relevant information to distinguish the type of phone number, but the information was not consistent in the file. Run the script with the .db file as first parameter and a non-existing .vcf file as second parameter. If the file exists, it will be overwritten without notice. To my big surprise, the resulting .vcf file was accepted by Google Contacts on the first try…
Of course you can also extract the contacts2.db file from e.g. a recent Nandroid backup.
Here is the (ugly, but working) code:
#!/usr/bin/env python2 import sqlite3 import sys import codecs class Contact: def __init__(self, id): self.id = id self.phoneNumbers = [] self.mailAddresses = [] self.lastname = "" self.firstname = "" self.hasPhone = False def GetPhones(self): return self.phoneNumbers def AddPhone(self, number): self.phoneNumbers.append(number) def AddMail(self, mail): self.mailAddresses.append(mail) def GetMails(self): return self.mailAddresses def SetFirstname(self, firstname): self.firstname = firstname def SetLastname(self, lastname): self.lastname = lastname def GetName(self): return (self.lastname, self.firstname) def GetId(self): return self.id def GetVCard(self): vcard = [] vcard.append("BEGIN:VCARD") vcard.append("VERSION:2.1") vcard.append("N:" + self.lastname + ";" + self.firstname) if len(self.lastname) > 0 and len(self.firstname) > 0: vcard.append("FN:" + self.firstname + " " + self.lastname) elif len(self.lastname) > 0: vcard.append("FN:" + self.lastname) elif len(self.firstname) > 0: vcard.append("FN:" + self.firstname) for phone in self.phoneNumbers: vcard.append("TEL;type=CELL:" + phone) for mail in self.mailAddresses: vcard.append("EMAIL;type=INTERNET;type=HOME:" + mail) vcard.append("END:VCARD") return vcard if len(sys.argv) < 3: sys.exit("Must give filenames as arguments") fn = sys.argv[1] db = sqlite3.Connection(fn) contacts = {} c = db.cursor() c.execute("SELECT COUNT(*) FROM contacts") num_entries = c.fetchone()[0] print "Getting " + str(num_entries) + " contacts..." c.execute("SELECT name_raw_contact_id, has_phone_number FROM contacts") while True: row = c.fetchone() if row is None: break if not contacts.has_key(row[0]): contacts[row[0]] = Contact(row[0]) contacts[row[0]].hasPhone = row[1] for key in contacts.keys(): c.execute("SELECT mimetype_id, data_version, data1, data2, data3 FROM data WHERE raw_contact_id = " + str(contacts[key].GetId())) while True: row = c.fetchone() if row is None: break if row[0] == 1: # Mail contacts[key].AddMail(row[2]) elif row[0] == 6: # Name if row[4]: contacts[key].SetLastname(row[4]) if row[3]: contacts[key].SetFirstname(row[3]) elif row[0] == 5: # Phone if row[3] == 2: # Mobile? pass elif row[3] == 1: # Home? pass contacts[key].AddPhone(row[2]) fp = codecs.open(sys.argv[2], "w", "utf-8") fp.write(u'\ufeff') print "Contact's Details: " for key in contacts.keys(): if contacts[key].GetId() != 0: string = "" last, first = contacts[key].GetName() if len(last) > 0 and len(first) > 0: string += last + " " + first elif len(last) > 0: string += last elif len(first) > 0: string += first phones = contacts[key].GetPhones() for phone in phones: string += phone + " " mails = contacts[key].GetMails() for mail in mails: string += mail + " " print string fp.write("\n".join(contacts[key].GetVCard())) fp.write("\n") fp.close() c.close() db.close()