Recovering contacts from dead Android phone

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()
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies

Discussion

Nikita, 2016/09/27 21:25

Use this Online converter contacts2.db to vCard http://gsmrecovery.ru/db2vc/index.php?lang=en

Ian Worthington, 2016/02/07 01:41

Hi Andreas

Many thanks for this script. Used it as the basis for my own, which extracts slightly more information. You can find it at:

http://forum.xda-developers.com/android/help/extract-contacts-backup-t3307684