Retrieve or backup Android (LineageOS) contacts with adb

Revision history
Tags: android lineageos sqlite

I have a broken phone that doesn’t register touch events anymore. I have a new phone now, but the old one has all the phone numbers. I tried to find a way to get the contacts out of there using adb.

For adb to work, adb debugging must be (or have been, in my case) enabled on the device. To enable developer settings, touch “Build number” in “About phone” 15 times. (See elsewhere for more information)

I needed root shell to access the /data folder on the device.

$ adb root
$ adb shell

Then in the root shell, I tried to find some files related to contacts

# find . -iname *contact* 2>/dev/null

Opening the .db file in a text editor showed that it was a sqlite3 database file. I backed it up with adb pull, then opened it up with sqlite3.

$ adb pull ./data/data/ ~/contacts
$ sqlite3 -readonly ~/contacts/contacts2.db

In the sqlite shell, you can use .tables to list all tables in the database, and .schema [name] to dump the schema. The interesting tables (as far as I could find) were contacts_raw and phone_lookup. I created a query to dump all the names and numers in the database.

SELECT DISTINCT c.display_name, n.normalized_number
FROM raw_contacts AS c
LEFT JOIN phone_lookup AS n
ON c._id=n.raw_contact_id
ORDER BY c.display_name;

This yielded a list of values similar to this:

Ola Nordmann|+4798765432
John Doe|+11800000000
Ola Nordmann|+4784422107

Some contacts are returned with multiple numbers, so I want to group them up and create vCard records for each of my contacts.

I wrote a script in Python to help me with this:

Prints a vCard collection from a file with list of names and numbers.
Expects a CSV (or pipe-separated) file containing a name and a number for each record.

Author: Stig Kolstad (, Aug 2019

## Example input file contents:

Ola Nordmann|+4798765432
John Doe|+11800000000
Ola Nordmann|+4784422107

## Resulting output string:

N:;Ola Nordmann;;;
N:;John Doe;;;
import argparse
import os
from collections import defaultdict

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('filename', help="Separated values file")
    parser.add_argument('-s', '--separator', help="Value separator (default |)", default='|')
    args = parser.parse_args()

    FILE = args.filename
    SEP  = args.separator

    contacts = defaultdict(list)

    # Collect all numbers for each distinct name into lists
    with open(FILE, mode='r') as f:
        for line in f:
            name, number = line.strip().split(SEP)

    # Output VCF formatted records for each contact
    for name, numbers in contacts.items():
        print(vcfstr(name, numbers))

def vcfstr(name=None, numbers=None):
    """Returns a VCF vCard 2.1 formatted string"""
    f = [ "BEGIN:VCARD", "VERSION:2.1", "N:;%s;;;" % name ]
    for n in numbers:
        f.append("TEL;CELL:%s" % n)

    return os.linesep.join(f)

if __name__ == "__main__":

Then I ran this script with the results returned from the sqlite query and saved it to a vcf file.

$ python contacts.txt > old-contacts.vcf
$ adb push old-contacts.vcf /data/Download/

Then imported it on my phone again, through the settings menu in the Contacts app

Contact app import prompt

After a little second, the contacts were successfully imported!

Import successful


If you have any comments or feedback, please send me an e-mail. (stig at stigok dotcom).

Did you find any typos, incorrect information, or have something to add? Then please propose a change to this post.

Creative Commons License This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.