Bug 1193431 - Remove unused Contact API related code r?rnewman draft
authorJonathan Almeida [:jonalmeida] <jalmeida@mozilla.com>
Tue, 11 Aug 2015 14:56:56 -0700
changeset 284863 82d0b32a866a73724bacdf656e8ae3d356019b1c
parent 284166 7a19194812eb767bee7cdf8fc36ba9a383c1bead
child 508294 c6b54b203f45c8a6a7db2029819d016178d70542
push id4346
push userjalmeida@mozilla.com
push dateThu, 13 Aug 2015 00:08:57 +0000
reviewersrnewman
bugs1193431
milestone42.0a1
Bug 1193431 - Remove unused Contact API related code r?rnewman
mobile/android/.eslintignore
mobile/android/base/ContactService.java
mobile/android/base/GeckoApp.java
mobile/android/base/moz.build
mobile/android/base/strings.xml.in
mobile/android/chrome/content/WebappRT.js
mobile/android/chrome/content/browser.js
mobile/android/installer/package-manifest.in
mobile/android/locales/en-US/chrome/browser.properties
mobile/android/modules/ContactService.jsm
mobile/android/modules/moz.build
--- a/mobile/android/.eslintignore
+++ b/mobile/android/.eslintignore
@@ -7,18 +7,15 @@ chrome/content/healthreport-prefs.js
 
 # Uses `#expand`
 chrome/content/about.js
 
 # Not much JS to lint and non-standard at that
 installer/
 locales/
 
-# Pretty sure we're disabling this one anyway
-modules/ContactService.jsm
-
 # es7 proposed: array comprehensions
 #   https://github.com/eslint/espree/issues/125
 modules/WebappManager.jsm
 
 # Non-standard `(catch ex if ...)`
 components/Snippets.js
 modules/MatchstickApp.jsm
deleted file mode 100644
--- a/mobile/android/base/ContactService.java
+++ /dev/null
@@ -1,2014 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-package org.mozilla.gecko;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map.Entry;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.util.GeckoEventListener;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.AlertDialog;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.DialogInterface;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Event;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Nickname;
-import android.provider.ContactsContract.CommonDataKinds.Note;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.RawContacts.Entity;
-import android.telephony.PhoneNumberUtils;
-import android.util.Log;
-
-public class ContactService implements GeckoEventListener {
-    private static final String LOGTAG = "GeckoContactService";
-    private static final boolean DEBUG = false;
-
-    private final static int GROUP_ACCOUNT_NAME = 0;
-    private final static int GROUP_ACCOUNT_TYPE = 1;
-    private final static int GROUP_ID = 2;
-    private final static int GROUP_TITLE = 3;
-    private final static int GROUP_AUTO_ADD = 4;
-
-    private final static String CARRIER_COLUMN = Data.DATA5;
-    private final static String CUSTOM_DATA_COLUMN = Data.DATA1;
-
-    // Pre-Honeycomb versions of Android have a "My Contacts" system group that all contacts are
-    // assigned to by default for a given account. After Honeycomb, an AUTO_ADD database column
-    // was added to denote groups that contacts are automatically added to
-    private final static String PRE_HONEYCOMB_DEFAULT_GROUP = "System Group: My Contacts";
-    private final static String MIMETYPE_ADDITIONAL_NAME = "org.mozilla.gecko/additional_name";
-    private final static String MIMETYPE_SEX = "org.mozilla.gecko/sex";
-    private final static String MIMETYPE_GENDER_IDENTITY = "org.mozilla.gecko/gender_identity";
-    private final static String MIMETYPE_KEY = "org.mozilla.gecko/key";
-    private final static String MIMETYPE_MOZILLA_CONTACTS_FLAG = "org.mozilla.gecko/contact_flag";
-
-    private final EventDispatcher mEventDispatcher;
-
-    private String mAccountName;
-    private String mAccountType;
-    private String mGroupTitle;
-    private long mGroupId;
-    private boolean mGotDeviceAccount;
-
-    private HashMap<String, String> mColumnNameConstantsMap;
-    private HashMap<String, String> mMimeTypeConstantsMap;
-    private HashMap<String, Integer> mAddressTypesMap;
-    private HashMap<String, Integer> mPhoneTypesMap;
-    private HashMap<String, Integer> mEmailTypesMap;
-    private HashMap<String, Integer> mWebsiteTypesMap;
-    private HashMap<String, Integer> mImTypesMap;
-
-    private final ContentResolver mContentResolver;
-    private final GeckoApp mActivity;
-
-    ContactService(EventDispatcher eventDispatcher, GeckoApp activity) {
-        mEventDispatcher = eventDispatcher;
-        mActivity = activity;
-        mContentResolver = mActivity.getContentResolver();
-
-        EventDispatcher.getInstance().registerGeckoThreadListener(this,
-            "Android:Contacts:Clear",
-            "Android:Contacts:Find",
-            "Android:Contacts:GetAll",
-            "Android:Contacts:GetCount",
-            "Android:Contact:Remove",
-            "Android:Contact:Save");
-    }
-
-    public void destroy() {
-        EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
-            "Android:Contacts:Clear",
-            "Android:Contacts:Find",
-            "Android:Contacts:GetAll",
-            "Android:Contacts:GetCount",
-            "Android:Contact:Remove",
-            "Android:Contact:Save");
-    }
-
-    @Override
-    public void handleMessage(final String event, final JSONObject message) {
-        // If the account chooser dialog needs shown to the user, the message handling becomes
-        // asynchronous so it needs posted to a background thread from the UI thread when the
-        // account chooser dialog is dismissed by the user.
-        Runnable handleMessage = new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    if (DEBUG) {
-                        Log.d(LOGTAG, "Event: " + event + "\nMessage: " + message.toString(3));
-                    }
-
-                    final JSONObject messageData = message.getJSONObject("data");
-                    final String requestID = messageData.getString("requestID");
-
-                    // Options may not exist for all operations
-                    JSONObject contactOptions = messageData.optJSONObject("options");
-
-                    if ("Android:Contacts:Find".equals(event)) {
-                        findContacts(contactOptions, requestID);
-                    } else if ("Android:Contacts:GetAll".equals(event)) {
-                        getAllContacts(messageData, requestID);
-                    } else if ("Android:Contacts:Clear".equals(event)) {
-                        clearAllContacts(contactOptions, requestID);
-                    } else if ("Android:Contact:Save".equals(event)) {
-                        saveContact(contactOptions, requestID);
-                    } else if ("Android:Contact:Remove".equals(event)) {
-                        removeContact(contactOptions, requestID);
-                    } else if ("Android:Contacts:GetCount".equals(event)) {
-                        getContactsCount(requestID);
-                    } else {
-                        throw new IllegalArgumentException("Unexpected event: " + event);
-                    }
-                } catch (JSONException e) {
-                    throw new IllegalArgumentException("Message: " + e);
-                }
-            }
-        };
-
-        // Get the account name/type if they haven't been set yet
-        if (!mGotDeviceAccount) {
-            getDeviceAccount(handleMessage);
-        } else {
-            handleMessage.run();
-        }
-    }
-
-    private void findContacts(final JSONObject contactOptions, final String requestID) {
-        long[] rawContactIds = findContactsRawIds(contactOptions);
-        Log.i(LOGTAG, "Got " + (rawContactIds != null ? rawContactIds.length : "null") + " raw contact IDs");
-
-        final String[] sortOptions = getSortOptionsFromJSON(contactOptions);
-
-        if (rawContactIds == null || sortOptions == null) {
-            sendCallbackToJavascript("Android:Contacts:Find:Return:KO", requestID, null, null);
-        } else {
-            sendCallbackToJavascript("Android:Contacts:Find:Return:OK", requestID,
-                                     new String[] {"contacts"},
-                                     new Object[] {getContactsAsJSONArray(rawContactIds, sortOptions[0],
-                                                                          sortOptions[1])});
-        }
-    }
-
-    private void getAllContacts(final JSONObject contactOptions, final String requestID) {
-        long[] rawContactIds = getAllRawContactIds();
-        Log.i(LOGTAG, "Got " + rawContactIds.length + " raw contact IDs");
-
-        final String[] sortOptions = getSortOptionsFromJSON(contactOptions);
-
-        if (rawContactIds == null || sortOptions == null) {
-            // There's no failure message for getAll
-            return;
-        } else {
-            sendCallbackToJavascript("Android:Contacts:GetAll:Next", requestID,
-                                     new String[] {"contacts"},
-                                     new Object[] {getContactsAsJSONArray(rawContactIds, sortOptions[0],
-                                                                          sortOptions[1])});
-        }
-    }
-
-    private static String[] getSortOptionsFromJSON(final JSONObject contactOptions) {
-        String sortBy = null;
-        String sortOrder = null;
-
-        try {
-            final JSONObject findOptions = contactOptions.getJSONObject("findOptions");
-            sortBy = findOptions.optString("sortBy").toLowerCase();
-            sortOrder = findOptions.optString("sortOrder").toLowerCase();
-
-            if ("".equals(sortBy)) {
-                sortBy = null;
-            }
-            if ("".equals(sortOrder)) {
-                sortOrder = "ascending";
-            }
-
-            // Only "familyname" and "givenname" are valid sortBy values and only "ascending"
-            // and "descending" are valid sortOrder values
-            if ((sortBy != null && !"familyname".equals(sortBy) && !"givenname".equals(sortBy)) ||
-                (!"ascending".equals(sortOrder) && !"descending".equals(sortOrder))) {
-                return null;
-            }
-        } catch (JSONException e) {
-            throw new IllegalArgumentException(e);
-        }
-
-        return new String[] {sortBy, sortOrder};
-    }
-
-    private long[] findContactsRawIds(final JSONObject contactOptions) {
-        List<Long> rawContactIds = new ArrayList<Long>();
-        Cursor cursor = null;
-
-        try {
-            final JSONObject findOptions = contactOptions.getJSONObject("findOptions");
-            String filterValue = findOptions.optString("filterValue");
-            JSONArray filterBy = findOptions.optJSONArray("filterBy");
-            final String filterOp = findOptions.optString("filterOp");
-            final int filterLimit = findOptions.getInt("filterLimit");
-            final int substringMatching = findOptions.getInt("substringMatching");
-
-            // If filter value is undefined, avoid all the logic below and just return
-            // all available raw contact IDs
-            if ("".equals(filterValue) || "".equals(filterOp)) {
-                long[] allRawContactIds = getAllRawContactIds();
-
-                // Truncate the raw contacts IDs array if necessary
-                if (filterLimit > 0 && allRawContactIds.length > filterLimit) {
-                    long[] truncatedRawContactIds = new long[filterLimit];
-                    System.arraycopy(allRawContactIds, 0, truncatedRawContactIds, 0, filterLimit);
-                    return truncatedRawContactIds;
-                }
-                return allRawContactIds;
-            }
-
-            // "match" can only be used with the "tel" field
-            if ("match".equals(filterOp)) {
-                for (int i = 0; i < filterBy.length(); i++) {
-                    if (!"tel".equals(filterBy.getString(i))) {
-                        Log.w(LOGTAG, "\"match\" filterBy option is only valid for the \"tel\" field");
-                        return null;
-                    }
-                }
-            }
-
-            // Only select contacts from the selected account
-            String selection = null;
-            String[] selectionArgs = null;
-
-            if (mAccountName != null) {
-                selection = RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?";
-                selectionArgs = new String[] {mAccountName, mAccountType};
-            }
-
-
-            final String[] columnsToGet;
-
-            // If a filterBy value was not specified, search all columns
-            if (filterBy == null || filterBy.length() == 0) {
-                columnsToGet = null;
-            } else {
-                // Only get the columns given in the filterBy array
-                List<String> columnsToGetList = new ArrayList<String>();
-
-                columnsToGetList.add(Data.RAW_CONTACT_ID);
-                columnsToGetList.add(Data.MIMETYPE);
-                for (int i = 0; i < filterBy.length(); i++) {
-                    final String field = filterBy.getString(i);
-
-                    // If one of the filterBy fields is the ID, just return the filter value
-                    // which should be the ID
-                    if ("id".equals(field)) {
-                        try {
-                            return new long[] {Long.valueOf(filterValue)};
-                        } catch (NumberFormatException e) {
-                            // If the ID couldn't be converted to a long, it's invalid data
-                            // so return null for failure
-                            return null;
-                        }
-                    }
-
-                    final String columnName = getColumnNameConstant(field);
-
-                    if (columnName != null) {
-                        columnsToGetList.add(columnName);
-                    } else {
-                        Log.w(LOGTAG, "Unknown filter option: " + field);
-                    }
-                }
-
-                columnsToGet = columnsToGetList.toArray(new String[columnsToGetList.size()]);
-            }
-
-            // Execute the query
-            cursor = mContentResolver.query(Data.CONTENT_URI, columnsToGet, selection,
-                                            selectionArgs, null);
-
-            if (cursor.getCount() > 0) {
-                cursor.moveToPosition(-1);
-                while (cursor.moveToNext()) {
-                    String mimeType = cursor.getString(cursor.getColumnIndex(Data.MIMETYPE));
-
-                    // Check if the current mimetype is one of the types to filter by
-                    if (filterBy != null && filterBy.length() > 0) {
-                        for (int i = 0; i < filterBy.length(); i++) {
-                            String currentFilterBy = filterBy.getString(i);
-
-                            if (mimeType.equals(getMimeTypeOfField(currentFilterBy))) {
-                                String columnName = getColumnNameConstant(currentFilterBy);
-                                int columnIndex = cursor.getColumnIndex(columnName);
-                                String databaseValue = cursor.getString(columnIndex);
-
-                                boolean isPhone = false;
-                                if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                                    isPhone = true;
-                                } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                                    // Translate the group ID to the group name for matching
-                                    try {
-                                        databaseValue = getGroupName(Long.valueOf(databaseValue));
-                                    } catch (NumberFormatException e) {
-                                        Log.e(LOGTAG, "Number Format Exception", e);
-                                        continue;
-                                    }
-                                } else if (databaseValue == null) {
-                                    continue;
-                                }
-
-                                // Check if the value matches the filter value
-                                if (isFindMatch(filterOp, filterValue, databaseValue, isPhone, substringMatching)) {
-                                    addMatchToList(cursor, rawContactIds);
-                                    break;
-                                }
-                            }
-                        }
-                    } else {
-                        // If no filterBy options were given, check each column for a match
-                        int numColumns = cursor.getColumnCount();
-                        for (int i = 0; i < numColumns; i++) {
-                            String databaseValue = cursor.getString(i);
-                            if (databaseValue != null && isFindMatch(filterOp, filterValue, databaseValue, false, substringMatching)) {
-                                addMatchToList(cursor, rawContactIds);
-                                break;
-                            }
-                        }
-                    }
-
-                    // If the max found contacts size has been hit, stop looking for contacts
-                    // A filter limit of 0 denotes there is no limit
-                    if (filterLimit > 0 && filterLimit <= rawContactIds.size()) {
-                        break;
-                    }
-                }
-            }
-        } catch (JSONException e) {
-            throw new IllegalArgumentException(e);
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
-
-        // Return the contact IDs list converted to an array
-        return convertLongListToArray(rawContactIds);
-    }
-
-    private boolean isFindMatch(final String filterOp, String filterValue, String databaseValue,
-                                final boolean isPhone, final int substringMatching) {
-        Log.i(LOGTAG, "matching: filterOp: " + filterOp);
-        if (DEBUG) {
-            Log.d(LOGTAG, "matching: filterValue: " + filterValue);
-            Log.d(LOGTAG, "matching: databaseValue: " + databaseValue);
-        }
-        Log.i(LOGTAG, "matching: isPhone: " + isPhone);
-        Log.i(LOGTAG, "matching: substringMatching: " + substringMatching);
-
-        if (databaseValue == null) {
-            return false;
-        }
-
-        filterValue = filterValue.toLowerCase();
-        databaseValue = databaseValue.toLowerCase();
-
-        switch (filterOp) {
-            case "match":
-                // If substring matching is a positive number, only pay attention to the last X characters
-                // of both the filter and database values
-                if (substringMatching > 0) {
-                    databaseValue = substringStartFromEnd(cleanPhoneNumber(databaseValue), substringMatching);
-                    filterValue = substringStartFromEnd(cleanPhoneNumber(filterValue), substringMatching);
-                    return databaseValue.startsWith(filterValue);
-                }
-
-                return databaseValue.equals(filterValue);
-            case "equals":
-                if (isPhone) {
-                    return PhoneNumberUtils.compare(filterValue, databaseValue);
-                }
-
-                return databaseValue.equals(filterValue);
-            case "contains":
-                if (isPhone) {
-                    filterValue = cleanPhoneNumber(filterValue);
-                    databaseValue = cleanPhoneNumber(databaseValue);
-                }
-
-                return databaseValue.contains(filterValue);
-            case "startsWith":
-                // If a phone number, remove non-dialable characters and then only pay attention to
-                // the last X digits given by the substring matching values (see bug 877302)
-                if (isPhone) {
-                    String cleanedDatabasePhone = cleanPhoneNumber(databaseValue);
-                    if (substringMatching > 0) {
-                        cleanedDatabasePhone = substringStartFromEnd(cleanedDatabasePhone, substringMatching);
-                    }
-
-                    if (cleanedDatabasePhone.startsWith(filterValue)) {
-                        return true;
-                    }
-                }
-
-                return databaseValue.startsWith(filterValue);
-        }
-
-        return false;
-    }
-
-    private static String cleanPhoneNumber(String phone) {
-        return phone.replace(" ", "").replace("(", "").replace(")", "").replace("-", "");
-    }
-
-    private static String substringStartFromEnd(final String string, final int distanceFromEnd) {
-        int stringLen = string.length();
-        if (stringLen < distanceFromEnd) {
-            return string;
-        }
-        return string.substring(stringLen - distanceFromEnd);
-    }
-
-    private static void addMatchToList(final Cursor cursor, List<Long> rawContactIds) {
-        long rawContactId = cursor.getLong(cursor.getColumnIndex(Data.RAW_CONTACT_ID));
-        if (!rawContactIds.contains(rawContactId)) {
-            rawContactIds.add(rawContactId);
-        }
-    }
-
-    private JSONArray getContactsAsJSONArray(final long[] rawContactIds, final String sortBy, final String sortOrder) {
-        List<JSONObject> contactsList = new ArrayList<JSONObject>();
-        JSONArray contactsArray = new JSONArray();
-
-        // Get each contact as a JSON object
-        for (int i = 0; i < rawContactIds.length; i++) {
-            contactsList.add(getContactAsJSONObject(rawContactIds[i]));
-        }
-
-        // Sort the contacts
-        if (sortBy != null) {
-            Collections.sort(contactsList, new ContactsComparator(sortBy, sortOrder));
-        }
-
-        // Convert the contacts list to a JSON array
-        for (int i = 0; i < contactsList.size(); i++) {
-            contactsArray.put(contactsList.get(i));
-        }
-
-        return contactsArray;
-    }
-
-    private JSONObject getContactAsJSONObject(long rawContactId) {
-        // ContactManager wants a contact object with it's properties wrapped in an array of objects
-        JSONObject contact = new JSONObject();
-        JSONObject contactProperties = new JSONObject();
-
-        JSONArray names = new JSONArray();
-        JSONArray givenNames = new JSONArray();
-        JSONArray familyNames = new JSONArray();
-        JSONArray honorificPrefixes = new JSONArray();
-        JSONArray honorificSuffixes = new JSONArray();
-        JSONArray additionalNames = new JSONArray();
-        JSONArray nicknames = new JSONArray();
-        JSONArray addresses = new JSONArray();
-        JSONArray phones = new JSONArray();
-        JSONArray emails = new JSONArray();
-        JSONArray organizations = new JSONArray();
-        JSONArray jobTitles = new JSONArray();
-        JSONArray notes = new JSONArray();
-        JSONArray urls = new JSONArray();
-        JSONArray impps = new JSONArray();
-        JSONArray categories = new JSONArray();
-        String bday = null;
-        String anniversary = null;
-        String sex = null;
-        String genderIdentity = null;
-        JSONArray key = new JSONArray();
-
-        // Get all the data columns
-        final String[] columnsToGet = getAllColumns();
-
-        Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
-        Uri entityUri = Uri.withAppendedPath(rawContactUri, Entity.CONTENT_DIRECTORY);
-
-        Cursor cursor = mContentResolver.query(entityUri, columnsToGet, null, null, null);
-        cursor.moveToPosition(-1);
-        while (cursor.moveToNext()) {
-            String mimeType = cursor.getString(cursor.getColumnIndex(Data.MIMETYPE));
-
-            // Put the proper fields for each mimetype into the JSON arrays
-            try {
-                if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    final String displayName = cursor.getString(cursor.getColumnIndex(StructuredName.DISPLAY_NAME));
-                    final String givenName = cursor.getString(cursor.getColumnIndex(StructuredName.GIVEN_NAME));
-                    final String familyName = cursor.getString(cursor.getColumnIndex(StructuredName.FAMILY_NAME));
-                    final String prefix = cursor.getString(cursor.getColumnIndex(StructuredName.PREFIX));
-                    final String suffix = cursor.getString(cursor.getColumnIndex(StructuredName.SUFFIX));
-
-                    if (displayName != null) {
-                        names.put(displayName);
-                    }
-                    if (givenName != null) {
-                        givenNames.put(givenName);
-                    }
-                    if (familyName != null) {
-                        familyNames.put(familyName);
-                    }
-                    if (prefix != null) {
-                        honorificPrefixes.put(prefix);
-                    }
-                    if (suffix != null) {
-                        honorificSuffixes.put(suffix);
-                    }
-
-                } else if (MIMETYPE_ADDITIONAL_NAME.equals(mimeType)) {
-                    additionalNames.put(cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN)));
-
-                } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    nicknames.put(cursor.getString(cursor.getColumnIndex(Nickname.NAME)));
-
-                } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    initAddressTypesMap();
-                    getAddressDataAsJSONObject(cursor, addresses);
-
-                } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    initPhoneTypesMap();
-                    getPhoneDataAsJSONObject(cursor, phones);
-
-                } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    initEmailTypesMap();
-                    getGenericDataAsJSONObject(cursor, emails, Email.ADDRESS, Email.TYPE, Email.LABEL, mEmailTypesMap);
-
-                } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    getOrganizationDataAsJSONObject(cursor, organizations, jobTitles);
-
-                } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    notes.put(cursor.getString(cursor.getColumnIndex(Note.NOTE)));
-
-                } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    initWebsiteTypesMap();
-                    getGenericDataAsJSONObject(cursor, urls, Website.URL, Website.TYPE, Website.LABEL, mWebsiteTypesMap);
-
-                } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    initImTypesMap();
-                    getGenericDataAsJSONObject(cursor, impps, Im.DATA, Im.TYPE, Im.LABEL, mImTypesMap);
-
-                } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    long groupId = cursor.getLong(cursor.getColumnIndex(GroupMembership.GROUP_ROW_ID));
-                    String groupName = getGroupName(groupId);
-                    if (!doesJSONArrayContainString(categories, groupName)) {
-                        categories.put(groupName);
-                    }
-
-                } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
-                    int type = cursor.getInt(cursor.getColumnIndex(Event.TYPE));
-                    String date = cursor.getString(cursor.getColumnIndex(Event.START_DATE));
-
-                    // Add the time info onto the date so it correctly parses into a JS date object
-                    date += "T00:00:00";
-
-                    switch (type) {
-                        case Event.TYPE_BIRTHDAY:
-                            bday = date;
-                            break;
-
-                        case Event.TYPE_ANNIVERSARY:
-                            anniversary = date;
-                            break;
-                    }
-
-                } else if (MIMETYPE_SEX.equals(mimeType)) {
-                    sex = cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN));
-
-                } else if (MIMETYPE_GENDER_IDENTITY.equals(mimeType)) {
-                    genderIdentity = cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN));
-
-                } else if (MIMETYPE_KEY.equals(mimeType)) {
-                    key.put(cursor.getString(cursor.getColumnIndex(CUSTOM_DATA_COLUMN)));
-                }
-            } catch (JSONException e) {
-                throw new IllegalArgumentException(e);
-            }
-        }
-        cursor.close();
-
-        try {
-            // Add the fields to the contact properties object
-            contactProperties.put("name", names);
-            contactProperties.put("givenName", givenNames);
-            contactProperties.put("familyName", familyNames);
-            contactProperties.put("honorificPrefix", honorificPrefixes);
-            contactProperties.put("honorificSuffix", honorificSuffixes);
-            contactProperties.put("additionalName", additionalNames);
-            contactProperties.put("nickname", nicknames);
-            contactProperties.put("adr", addresses);
-            contactProperties.put("tel", phones);
-            contactProperties.put("email", emails);
-            contactProperties.put("org", organizations);
-            contactProperties.put("jobTitle", jobTitles);
-            contactProperties.put("note", notes);
-            contactProperties.put("url", urls);
-            contactProperties.put("impp", impps);
-            contactProperties.put("category", categories);
-            contactProperties.put("key", key);
-
-            putPossibleNullValueInJSONObject("bday", bday, contactProperties);
-            putPossibleNullValueInJSONObject("anniversary", anniversary, contactProperties);
-            putPossibleNullValueInJSONObject("sex", sex, contactProperties);
-            putPossibleNullValueInJSONObject("genderIdentity", genderIdentity, contactProperties);
-
-            // Add the raw contact ID and the properties to the contact
-            contact.put("id", String.valueOf(rawContactId));
-            contact.put("updated", null);
-            contact.put("published", null);
-            contact.put("properties", contactProperties);
-        } catch (JSONException e) {
-            throw new IllegalArgumentException(e);
-        }
-
-        if (DEBUG) {
-            try {
-                Log.d(LOGTAG, "Got contact: " + contact.toString(3));
-            } catch (JSONException e) {}
-        }
-
-        return contact;
-    }
-
-    private boolean bool(int integer) {
-        return integer != 0;
-    }
-
-    private void getGenericDataAsJSONObject(Cursor cursor, JSONArray array, final String dataColumn,
-                                            final String typeColumn, final String typeLabelColumn,
-                                            final HashMap<String, Integer> typeMap) throws JSONException {
-        String value = cursor.getString(cursor.getColumnIndex(dataColumn));
-        int typeConstant = cursor.getInt(cursor.getColumnIndex(typeColumn));
-        String type;
-        if (typeConstant == BaseTypes.TYPE_CUSTOM) {
-            type = cursor.getString(cursor.getColumnIndex(typeLabelColumn));
-        } else {
-            type = getKeyFromMapValue(typeMap, typeConstant);
-        }
-
-        // Since an object may have multiple types, it may have already been added,
-        // but still needs the new type added
-        boolean found = false;
-        if (type != null) {
-            for (int i = 0; i < array.length(); i++) {
-                JSONObject object = array.getJSONObject(i);
-                if (value.equals(object.getString("value"))) {
-                    found = true;
-
-                    JSONArray types = object.getJSONArray("type");
-                    if (!doesJSONArrayContainString(types, type)) {
-                        types.put(type);
-                        break;
-                    }
-                }
-            }
-        }
-
-        // If an existing object wasn't found, make a new one
-        if (!found) {
-            JSONObject object = new JSONObject();
-            JSONArray types = new JSONArray();
-            object.put("value", value);
-            types.put(type);
-            object.put("type", types);
-            object.put("pref", bool(cursor.getInt(cursor.getColumnIndex(Data.IS_SUPER_PRIMARY))));
-
-            array.put(object);
-        }
-    }
-
-    private void getPhoneDataAsJSONObject(Cursor cursor, JSONArray phones) throws JSONException {
-        String value = cursor.getString(cursor.getColumnIndex(Phone.NUMBER));
-        int typeConstant = cursor.getInt(cursor.getColumnIndex(Phone.TYPE));
-        String type;
-        if (typeConstant == Phone.TYPE_CUSTOM) {
-            type = cursor.getString(cursor.getColumnIndex(Phone.LABEL));
-        } else {
-            type = getKeyFromMapValue(mPhoneTypesMap, typeConstant);
-        }
-
-        // Since a phone may have multiple types, it may have already been added,
-        // but still needs the new type added
-        boolean found = false;
-        if (type != null) {
-            for (int i = 0; i < phones.length(); i++) {
-                JSONObject phone = phones.getJSONObject(i);
-                if (value.equals(phone.getString("value"))) {
-                    found = true;
-
-                    JSONArray types = phone.getJSONArray("type");
-                    if (!doesJSONArrayContainString(types, type)) {
-                        types.put(type);
-                        break;
-                    }
-                }
-            }
-        }
-
-        // If an existing phone wasn't found, make a new one
-        if (!found) {
-            JSONObject phone = new JSONObject();
-            JSONArray types = new JSONArray();
-            phone.put("value", value);
-            phone.put("type", type);
-            types.put(type);
-            phone.put("type", types);
-            phone.put("carrier", cursor.getString(cursor.getColumnIndex(CARRIER_COLUMN)));
-            phone.put("pref", bool(cursor.getInt(cursor.getColumnIndex(Phone.IS_SUPER_PRIMARY))));
-
-            phones.put(phone);
-        }
-    }
-
-    private void getAddressDataAsJSONObject(Cursor cursor, JSONArray addresses) throws JSONException {
-        String streetAddress = cursor.getString(cursor.getColumnIndex(StructuredPostal.STREET));
-        String locality = cursor.getString(cursor.getColumnIndex(StructuredPostal.CITY));
-        String region = cursor.getString(cursor.getColumnIndex(StructuredPostal.REGION));
-        String postalCode = cursor.getString(cursor.getColumnIndex(StructuredPostal.POSTCODE));
-        String countryName = cursor.getString(cursor.getColumnIndex(StructuredPostal.COUNTRY));
-        int typeConstant = cursor.getInt(cursor.getColumnIndex(StructuredPostal.TYPE));
-        String type;
-        if (typeConstant == StructuredPostal.TYPE_CUSTOM) {
-            type = cursor.getString(cursor.getColumnIndex(StructuredPostal.LABEL));
-        } else {
-            type = getKeyFromMapValue(mAddressTypesMap, typeConstant);
-        }
-
-        // Since an email may have multiple types, it may have already been added,
-        // but still needs the new type added
-        boolean found = false;
-        if (type != null) {
-            for (int i = 0; i < addresses.length(); i++) {
-                JSONObject address = addresses.getJSONObject(i);
-                if (streetAddress.equals(address.getString("streetAddress")) &&
-                    locality.equals(address.getString("locality")) &&
-                    region.equals(address.getString("region")) &&
-                    countryName.equals(address.getString("countryName")) &&
-                    postalCode.equals(address.getString("postalCode"))) {
-                    found = true;
-
-                    JSONArray types = address.getJSONArray("type");
-                    if (!doesJSONArrayContainString(types, type)) {
-                        types.put(type);
-                        break;
-                    }
-                }
-            }
-        }
-
-        // If an existing email wasn't found, make a new one
-        if (!found) {
-            JSONObject address = new JSONObject();
-            JSONArray types = new JSONArray();
-            address.put("streetAddress", streetAddress);
-            address.put("locality", locality);
-            address.put("region", region);
-            address.put("countryName", countryName);
-            address.put("postalCode", postalCode);
-            types.put(type);
-            address.put("type", types);
-            address.put("pref", bool(cursor.getInt(cursor.getColumnIndex(StructuredPostal.IS_SUPER_PRIMARY))));
-
-            addresses.put(address);
-        }
-    }
-
-    private void getOrganizationDataAsJSONObject(Cursor cursor, JSONArray organizations,
-                                                 JSONArray jobTitles) throws JSONException {
-        int organizationColumnIndex = cursor.getColumnIndex(Organization.COMPANY);
-        int titleColumnIndex = cursor.getColumnIndex(Organization.TITLE);
-
-        if (!cursor.isNull(organizationColumnIndex)) {
-            organizations.put(cursor.getString(organizationColumnIndex));
-        }
-        if (!cursor.isNull(titleColumnIndex)) {
-            jobTitles.put(cursor.getString(titleColumnIndex));
-        }
-    }
-
-    private class ContactsComparator implements Comparator<JSONObject> {
-        final String mSortBy;
-        final String mSortOrder;
-
-        public ContactsComparator(final String sortBy, final String sortOrder) {
-            mSortBy = sortBy.toLowerCase();
-            mSortOrder = sortOrder.toLowerCase();
-        }
-
-        @Override
-        public int compare(JSONObject left, JSONObject right) {
-            // Determine if sorting by "family name, given name" or "given name, family name"
-            boolean familyFirst = false;
-            if ("familyname".equals(mSortBy)) {
-                familyFirst = true;
-            }
-
-            JSONObject leftProperties;
-            JSONObject rightProperties;
-            try {
-                leftProperties = left.getJSONObject("properties");
-                rightProperties = right.getJSONObject("properties");
-            } catch (JSONException e) {
-                throw new IllegalArgumentException(e);
-            }
-
-            JSONArray leftFamilyNames = leftProperties.optJSONArray("familyName");
-            JSONArray leftGivenNames = leftProperties.optJSONArray("givenName");
-            JSONArray rightFamilyNames = rightProperties.optJSONArray("familyName");
-            JSONArray rightGivenNames = rightProperties.optJSONArray("givenName");
-
-            // If any of the name arrays didn't exist (are null), create empty arrays
-            // to avoid doing a bunch of null checking below
-            if (leftFamilyNames == null) {
-                leftFamilyNames = new JSONArray();
-            }
-            if (leftGivenNames == null) {
-                leftGivenNames = new JSONArray();
-            }
-            if (rightFamilyNames == null) {
-                rightFamilyNames = new JSONArray();
-            }
-            if (rightGivenNames == null) {
-                rightGivenNames = new JSONArray();
-            }
-
-            int maxArrayLength = max(leftFamilyNames.length(), leftGivenNames.length(),
-                                     rightFamilyNames.length(), rightGivenNames.length());
-
-            int index = 0;
-            int compareResult;
-            do {
-                // Join together the given name and family name per the pattern above
-                String leftName = "";
-                String rightName = "";
-
-                if (familyFirst) {
-                    leftName = leftFamilyNames.optString(index, "") + leftGivenNames.optString(index, "");
-                    rightName = rightFamilyNames.optString(index, "") + rightGivenNames.optString(index, "");
-                } else {
-                    leftName = leftGivenNames.optString(index, "") + leftFamilyNames.optString(index, "");
-                    rightName = rightGivenNames.optString(index, "") + rightFamilyNames.optString(index, "");
-                }
-
-                index++;
-                compareResult = leftName.compareTo(rightName);
-
-            } while (compareResult == 0 && index < maxArrayLength);
-
-            // If descending order, flip the result
-            if (compareResult != 0 && "descending".equals(mSortOrder)) {
-                compareResult = -compareResult;
-            }
-
-            return compareResult;
-        }
-    }
-
-    private void clearAllContacts(final JSONObject contactOptions, final String requestID) {
-        ArrayList<ContentProviderOperation> deleteOptions = new ArrayList<ContentProviderOperation>();
-
-        // Delete all contacts from the selected account
-        ContentProviderOperation.Builder deleteOptionsBuilder = ContentProviderOperation.newDelete(RawContacts.CONTENT_URI);
-        if (mAccountName != null) {
-            deleteOptionsBuilder.withSelection(RawContacts.ACCOUNT_NAME + "=?", new String[] {mAccountName})
-                                .withSelection(RawContacts.ACCOUNT_TYPE + "=?", new String[] {mAccountType});
-        }
-
-        deleteOptions.add(deleteOptionsBuilder.build());
-
-        // Clear the contacts
-        String returnStatus = "KO";
-        if (applyBatch(deleteOptions) != null) {
-            returnStatus = "OK";
-        }
-
-        Log.i(LOGTAG, "Sending return status: " + returnStatus);
-
-        sendCallbackToJavascript("Android:Contacts:Clear:Return:" + returnStatus, requestID,
-                                 new String[] {"contactID"}, new Object[] {"undefined"});
-
-    }
-
-    private boolean deleteContact(String rawContactId) {
-        ContentProviderOperation deleteOptions = ContentProviderOperation.newDelete(RawContacts.CONTENT_URI)
-                                                 .withSelection(RawContacts._ID + "=?",
-                                                 new String[] {rawContactId})
-                                                 .build();
-
-        ArrayList<ContentProviderOperation> deleteOptionsList = new ArrayList<ContentProviderOperation>();
-        deleteOptionsList.add(deleteOptions);
-
-        return checkForPositiveCountInResults(applyBatch(deleteOptionsList));
-    }
-
-    private void removeContact(final JSONObject contactOptions, final String requestID) {
-        String rawContactId;
-        try {
-            rawContactId = contactOptions.getString("id");
-            Log.i(LOGTAG, "Removing contact with ID: " + rawContactId);
-        } catch (JSONException e) {
-            // We can't continue without a raw contact ID
-            sendCallbackToJavascript("Android:Contact:Remove:Return:KO", requestID, null, null);
-            return;
-        }
-
-        String returnStatus = "KO";
-        if(deleteContact(rawContactId)) {
-            returnStatus = "OK";
-        }
-
-        sendCallbackToJavascript("Android:Contact:Remove:Return:" + returnStatus, requestID,
-                                 new String[] {"contactID"}, new Object[] {rawContactId});
-    }
-
-    private void saveContact(final JSONObject contactOptions, final String requestID) {
-        try {
-            String reason = contactOptions.getString("reason");
-            JSONObject contact = contactOptions.getJSONObject("contact");
-            JSONObject contactProperties = contact.getJSONObject("properties");
-
-            if ("update".equals(reason)) {
-                updateContact(contactProperties, contact.getLong("id"), requestID);
-            } else {
-                insertContact(contactProperties, requestID);
-            }
-        } catch (JSONException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    private void insertContact(final JSONObject contactProperties, final String requestID) throws JSONException {
-        ArrayList<ContentProviderOperation> newContactOptions = new ArrayList<ContentProviderOperation>();
-
-        // Account to save the contact under
-        newContactOptions.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
-                         .withValue(RawContacts.ACCOUNT_NAME, mAccountName)
-                         .withValue(RawContacts.ACCOUNT_TYPE, mAccountType)
-                         .build());
-
-        List<ContentValues> newContactValues = getContactValues(contactProperties);
-
-        for (ContentValues values : newContactValues) {
-            newContactOptions.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
-                                  .withValueBackReference(Data.RAW_CONTACT_ID, 0)
-                                  .withValues(values)
-                                  .build());
-        }
-
-        String returnStatus = "KO";
-        Long newRawContactId = -1L;
-
-        // Insert the contact!
-        ContentProviderResult[] insertResults = applyBatch(newContactOptions);
-
-        if (insertResults != null) {
-            try {
-                // Get the ID of the newly created contact
-                newRawContactId = getRawContactIdFromContentProviderResults(insertResults);
-
-                if (newRawContactId != null) {
-                    returnStatus = "OK";
-                }
-            } catch (NumberFormatException e) {
-                Log.e(LOGTAG, "NumberFormatException", e);
-            }
-
-            Log.i(LOGTAG, "Newly created contact ID: " + newRawContactId);
-        }
-
-        Log.i(LOGTAG, "Sending return status: " + returnStatus);
-
-        sendCallbackToJavascript("Android:Contact:Save:Return:" + returnStatus, requestID,
-                                 new String[] {"contactID", "reason"},
-                                 new Object[] {newRawContactId, "create"});
-    }
-
-    private void updateContact(final JSONObject contactProperties, final long rawContactId, final String requestID) throws JSONException {
-        // Why is updating a contact so weird and horribly inefficient? Because Android doesn't
-        // like multiple values for contact fields, but the Mozilla contacts API calls for this.
-        // This means the Android update function is essentially completely useless. Why not just
-        // delete the contact and re-insert it? Because that would change the contact ID and the
-        // Mozilla contacts API shouldn't have this behavior. The solution is to delete each
-        // row from the contacts data table that belongs to the contact, and insert the new
-        // fields. But then why not just delete all the data from the data in one go and
-        // insert the new data in another? Because if all the data relating to a contact is
-        // deleted, Android will "conveniently" remove the ID making it impossible to insert data
-        // under the old ID. To work around this, we put a Mozilla contact flag in the database
-
-        ContentProviderOperation removeOptions = ContentProviderOperation.newDelete(Data.CONTENT_URI)
-                                                 .withSelection(Data.RAW_CONTACT_ID + "=? AND " +
-                                                 Data.MIMETYPE + " != '" + MIMETYPE_MOZILLA_CONTACTS_FLAG + "'",
-                                                 new String[] {String.valueOf(rawContactId)})
-                                                 .build();
-
-        ArrayList<ContentProviderOperation> removeOptionsList = new ArrayList<ContentProviderOperation>();
-        removeOptionsList.add(removeOptions);
-
-        ContentProviderResult[] removeResults = applyBatch(removeOptionsList);
-
-        // Check if the remove failed
-        if (removeResults == null || !checkForPositiveCountInResults(removeResults)) {
-            Log.w(LOGTAG, "Null or 0 remove results");
-
-            sendCallbackToJavascript("Android:Contact:Save:Return:KO", requestID, null, null);
-            return;
-        }
-
-        List<ContentValues> updateContactValues = getContactValues(contactProperties);
-        ArrayList<ContentProviderOperation> updateContactOptions = new ArrayList<ContentProviderOperation>();
-
-        for (ContentValues values : updateContactValues) {
-            updateContactOptions.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
-                                  .withValue(Data.RAW_CONTACT_ID, rawContactId)
-                                  .withValues(values)
-                                  .build());
-        }
-
-        String returnStatus = "KO";
-
-        // Update the contact!
-        applyBatch(updateContactOptions);
-
-        sendCallbackToJavascript("Android:Contact:Save:Return:OK", requestID,
-                                 new String[] {"contactID", "reason"},
-                                 new Object[] {rawContactId, "update"});
-    }
-
-    private List<ContentValues> getContactValues(final JSONObject contactProperties) throws JSONException {
-        List<ContentValues> contactValues = new ArrayList<ContentValues>();
-
-        // Add the contact to the default group so it is shown in other apps
-        // like the Contacts or People app
-        ContentValues defaultGroupValues = new ContentValues();
-        defaultGroupValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
-        defaultGroupValues.put(GroupMembership.GROUP_ROW_ID, mGroupId);
-        contactValues.add(defaultGroupValues);
-
-        // Create all the values that will be inserted into the new contact
-        getNameValues(contactProperties.optJSONArray("name"),
-                      contactProperties.optJSONArray("givenName"),
-                      contactProperties.optJSONArray("familyName"),
-                      contactProperties.optJSONArray("honorificPrefix"),
-                      contactProperties.optJSONArray("honorificSuffix"),
-                      contactValues);
-
-        getGenericValues(MIMETYPE_ADDITIONAL_NAME, CUSTOM_DATA_COLUMN,
-                         contactProperties.optJSONArray("additionalName"), contactValues);
-
-        getNicknamesValues(contactProperties.optJSONArray("nickname"), contactValues);
-
-        getAddressesValues(contactProperties.optJSONArray("adr"), contactValues);
-
-        getPhonesValues(contactProperties.optJSONArray("tel"), contactValues);
-
-        getEmailsValues(contactProperties.optJSONArray("email"), contactValues);
-
-        //getPhotosValues(contactProperties.optJSONArray("photo"), contactValues);
-
-        getGenericValues(Organization.CONTENT_ITEM_TYPE, Organization.COMPANY,
-                         contactProperties.optJSONArray("org"), contactValues);
-
-        getGenericValues(Organization.CONTENT_ITEM_TYPE, Organization.TITLE,
-                         contactProperties.optJSONArray("jobTitle"), contactValues);
-
-        getNotesValues(contactProperties.optJSONArray("note"), contactValues);
-
-        getWebsitesValues(contactProperties.optJSONArray("url"), contactValues);
-
-        getImsValues(contactProperties.optJSONArray("impp"), contactValues);
-
-        getCategoriesValues(contactProperties.optJSONArray("category"), contactValues);
-
-        getEventValues(contactProperties.optString("bday"), Event.TYPE_BIRTHDAY, contactValues);
-
-        getEventValues(contactProperties.optString("anniversary"), Event.TYPE_ANNIVERSARY, contactValues);
-
-        getCustomMimetypeValues(contactProperties.optString("sex"), MIMETYPE_SEX, contactValues);
-
-        getCustomMimetypeValues(contactProperties.optString("genderIdentity"), MIMETYPE_GENDER_IDENTITY, contactValues);
-
-        getGenericValues(MIMETYPE_KEY, CUSTOM_DATA_COLUMN, contactProperties.optJSONArray("key"),
-                         contactValues);
-
-        return contactValues;
-    }
-
-    private void getGenericValues(final String mimeType, final String dataType, final JSONArray fields,
-                                  List<ContentValues> newContactValues) throws JSONException {
-        if (fields == null) {
-            return;
-        }
-
-        for (int i = 0; i < fields.length(); i++) {
-            ContentValues contentValues = new ContentValues();
-            contentValues.put(Data.MIMETYPE, mimeType);
-            contentValues.put(dataType, fields.getString(i));
-            newContactValues.add(contentValues);
-        }
-    }
-
-    private void getNameValues(final JSONArray displayNames, final JSONArray givenNames,
-                               final JSONArray familyNames, final JSONArray prefixes,
-                               final JSONArray suffixes, List<ContentValues> newContactValues) throws JSONException {
-        int maxLen = max((displayNames != null ? displayNames.length() : 0),
-                         (givenNames != null ? givenNames.length() : 0),
-                         (familyNames != null ? familyNames.length() : 0),
-                         (prefixes != null ? prefixes.length() : 0),
-                         (suffixes != null ? suffixes.length() : 0));
-
-        for (int i = 0; i < maxLen; i++) {
-            ContentValues contentValues = new ContentValues();
-            contentValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
-
-            final String displayName = (displayNames != null ? displayNames.optString(i, null) : null);
-            final String givenName = (givenNames != null ? givenNames.optString(i, null) : null);
-            final String familyName = (familyNames != null ? familyNames.optString(i, null) : null);
-            final String prefix = (prefixes != null ? prefixes.optString(i, null) : null);
-            final String suffix = (suffixes != null ? suffixes.optString(i, null) : null);
-
-            if (displayName != null) {
-                contentValues.put(StructuredName.DISPLAY_NAME, displayName);
-            }
-            if (givenName != null) {
-                contentValues.put(StructuredName.GIVEN_NAME, givenName);
-            }
-            if (familyName != null) {
-                contentValues.put(StructuredName.FAMILY_NAME, familyName);
-            }
-            if (prefix != null) {
-                contentValues.put(StructuredName.PREFIX, prefix);
-            }
-            if (suffix != null) {
-                contentValues.put(StructuredName.SUFFIX, suffix);
-            }
-
-            newContactValues.add(contentValues);
-        }
-    }
-
-    private void getNicknamesValues(final JSONArray nicknames, List<ContentValues> newContactValues) throws JSONException {
-        if (nicknames == null) {
-            return;
-        }
-
-        for (int i = 0; i < nicknames.length(); i++) {
-            ContentValues contentValues = new ContentValues();
-            contentValues.put(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE);
-            contentValues.put(Nickname.NAME, nicknames.getString(i));
-            contentValues.put(Nickname.TYPE, Nickname.TYPE_DEFAULT);
-            newContactValues.add(contentValues);
-        }
-    }
-
-    private void getAddressesValues(final JSONArray addresses, List<ContentValues> newContactValues) throws JSONException {
-        if (addresses == null) {
-            return;
-        }
-
-        for (int i = 0; i < addresses.length(); i++) {
-            JSONObject address = addresses.getJSONObject(i);
-            JSONArray addressTypes = address.optJSONArray("type");
-
-            if (addressTypes != null) {
-                for (int j = 0; j < addressTypes.length(); j++) {
-                    // Translate the address type string to an integer constant
-                    // provided by the ContactsContract API
-                    final String type = addressTypes.getString(j);
-                    final int typeConstant = getAddressType(type);
-
-                    newContactValues.add(createAddressContentValues(address, typeConstant, type));
-                }
-            } else {
-                newContactValues.add(createAddressContentValues(address, -1, null));
-            }
-        }
-    }
-
-    private ContentValues createAddressContentValues(final JSONObject address, final int typeConstant,
-                                                     final String type) throws JSONException {
-        ContentValues contentValues = new ContentValues();
-        contentValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
-        contentValues.put(StructuredPostal.STREET, address.optString("streetAddress"));
-        contentValues.put(StructuredPostal.CITY, address.optString("locality"));
-        contentValues.put(StructuredPostal.REGION, address.optString("region"));
-        contentValues.put(StructuredPostal.POSTCODE, address.optString("postalCode"));
-        contentValues.put(StructuredPostal.COUNTRY, address.optString("countryName"));
-
-        if (type != null) {
-            contentValues.put(StructuredPostal.TYPE, typeConstant);
-
-            // If a custom type, add a label
-            if (typeConstant == BaseTypes.TYPE_CUSTOM) {
-                contentValues.put(StructuredPostal.LABEL, type);
-            }
-        }
-
-        if (address.has("pref")) {
-            contentValues.put(Data.IS_SUPER_PRIMARY, address.getBoolean("pref") ? 1 : 0);
-        }
-
-        return contentValues;
-    }
-
-    private void getPhonesValues(final JSONArray phones, List<ContentValues> newContactValues) throws JSONException {
-        if (phones == null) {
-            return;
-        }
-
-        for (int i = 0; i < phones.length(); i++) {
-            JSONObject phone = phones.getJSONObject(i);
-            JSONArray phoneTypes = phone.optJSONArray("type");
-            ContentValues contentValues;
-
-            if (phoneTypes != null && phoneTypes.length() > 0) {
-                for (int j = 0; j < phoneTypes.length(); j++) {
-                    // Translate the phone type string to an integer constant
-                    // provided by the ContactsContract API
-                    final String type = phoneTypes.getString(j);
-                    final int typeConstant = getPhoneType(type);
-
-                    contentValues = createContentValues(Phone.CONTENT_ITEM_TYPE, phone.optString("value"),
-                                                        typeConstant, type, phone.optBoolean("pref"));
-                    if (phone.has("carrier")) {
-                        contentValues.put(CARRIER_COLUMN, phone.optString("carrier"));
-                    }
-                    newContactValues.add(contentValues);
-                }
-            } else {
-                contentValues = createContentValues(Phone.CONTENT_ITEM_TYPE, phone.optString("value"),
-                                                    -1, null, phone.optBoolean("pref"));
-                if (phone.has("carrier")) {
-                    contentValues.put(CARRIER_COLUMN, phone.optString("carrier"));
-                }
-                newContactValues.add(contentValues);
-            }
-        }
-    }
-
-    private void getEmailsValues(final JSONArray emails, List<ContentValues> newContactValues) throws JSONException {
-        if (emails == null) {
-            return;
-        }
-
-        for (int i = 0; i < emails.length(); i++) {
-            JSONObject email = emails.getJSONObject(i);
-            JSONArray emailTypes = email.optJSONArray("type");
-
-            if (emailTypes != null && emailTypes.length() > 0) {
-                for (int j = 0; j < emailTypes.length(); j++) {
-                    // Translate the email type string to an integer constant
-                    // provided by the ContactsContract API
-                    final String type = emailTypes.getString(j);
-                    final int typeConstant = getEmailType(type);
-
-                    newContactValues.add(createContentValues(Email.CONTENT_ITEM_TYPE,
-                                                             email.optString("value"),
-                                                             typeConstant, type,
-                                                             email.optBoolean("pref")));
-                }
-            } else {
-                newContactValues.add(createContentValues(Email.CONTENT_ITEM_TYPE,
-                                                         email.optString("value"),
-                                                         -1, null, email.optBoolean("pref")));
-            }
-        }
-    }
-
-    private void getPhotosValues(final JSONArray photos, List<ContentValues> newContactValues) throws JSONException {
-        if (photos == null) {
-            return;
-        }
-
-        // TODO: implement this
-    }
-
-    private void getNotesValues(final JSONArray notes, List<ContentValues> newContactValues) throws JSONException {
-        if (notes == null) {
-            return;
-        }
-
-        for (int i = 0; i < notes.length(); i++) {
-            ContentValues contentValues = new ContentValues();
-            contentValues.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
-            contentValues.put(Note.NOTE, notes.getString(i));
-            newContactValues.add(contentValues);
-        }
-    }
-
-    private void getWebsitesValues(final JSONArray websites, List<ContentValues> newContactValues) throws JSONException {
-        if (websites == null) {
-            return;
-        }
-
-        for (int i = 0; i < websites.length(); i++) {
-            JSONObject website = websites.getJSONObject(i);
-            JSONArray websiteTypes = website.optJSONArray("type");
-
-            if (websiteTypes != null && websiteTypes.length() > 0) {
-                for (int j = 0; j < websiteTypes.length(); j++) {
-                    // Translate the website type string to an integer constant
-                    // provided by the ContactsContract API
-                    final String type = websiteTypes.getString(j);
-                    final int typeConstant = getWebsiteType(type);
-
-                    newContactValues.add(createContentValues(Website.CONTENT_ITEM_TYPE,
-                                                             website.optString("value"),
-                                                             typeConstant, type,
-                                                             website.optBoolean("pref")));
-                }
-            } else {
-                newContactValues.add(createContentValues(Website.CONTENT_ITEM_TYPE,
-                                                         website.optString("value"),
-                                                         -1, null, website.optBoolean("pref")));
-            }
-        }
-    }
-
-    private void getImsValues(final JSONArray ims, List<ContentValues> newContactValues) throws JSONException {
-        if (ims == null) {
-            return;
-        }
-
-        for (int i = 0; i < ims.length(); i++) {
-            JSONObject im = ims.getJSONObject(i);
-            JSONArray imTypes = im.optJSONArray("type");
-
-            if (imTypes != null && imTypes.length() > 0) {
-                for (int j = 0; j < imTypes.length(); j++) {
-                    // Translate the IM type string to an integer constant
-                    // provided by the ContactsContract API
-                    final String type = imTypes.getString(j);
-                    final int typeConstant = getImType(type);
-
-                    newContactValues.add(createContentValues(Im.CONTENT_ITEM_TYPE,
-                                                             im.optString("value"),
-                                                             typeConstant, type,
-                                                             im.optBoolean("pref")));
-                }
-            } else {
-                newContactValues.add(createContentValues(Im.CONTENT_ITEM_TYPE,
-                                                         im.optString("value"),
-                                                         -1, null, im.optBoolean("pref")));
-            }
-        }
-    }
-
-    private void getCategoriesValues(final JSONArray categories, List<ContentValues> newContactValues) throws JSONException {
-        if (categories == null) {
-            return;
-        }
-
-        for (int i = 0; i < categories.length(); i++) {
-            String category = categories.getString(i);
-
-            if ("my contacts".equals(category.toLowerCase()) ||
-                PRE_HONEYCOMB_DEFAULT_GROUP.equalsIgnoreCase(category)) {
-                Log.w(LOGTAG, "New contacts are implicitly added to the default group.");
-                continue;
-            }
-
-            // Find the group ID of the given category
-            long groupId = getGroupId(category);
-
-            // Create the group if it doesn't already exist
-            if (groupId == -1) {
-                groupId = createGroup(category);
-                // If the group is still -1, we failed to create the group
-                if (groupId == -1) {
-                    // Only log the category name if in debug
-                    if (DEBUG) {
-                        Log.d(LOGTAG, "Failed to create new group for category \"" + category + "\"");
-                    } else {
-                        Log.w(LOGTAG, "Failed to create new group for given category.");
-                    }
-                    continue;
-                }
-            }
-
-            ContentValues contentValues = new ContentValues();
-            contentValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
-            contentValues.put(GroupMembership.GROUP_ROW_ID, groupId);
-            newContactValues.add(contentValues);
-
-            newContactValues.add(contentValues);
-        }
-    }
-
-    private void getEventValues(final String event, final int type, List<ContentValues> newContactValues) {
-        if (event == null || event.length() < 11) {
-            return;
-        }
-
-        ContentValues contentValues = new ContentValues();
-        contentValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
-        contentValues.put(Event.START_DATE, event.substring(0, 10));
-        contentValues.put(Event.TYPE, type);
-        newContactValues.add(contentValues);
-    }
-
-    private void getCustomMimetypeValues(final String value, final String mimeType, List<ContentValues> newContactValues) {
-        if (value == null || "null".equals(value)) {
-            return;
-        }
-
-        ContentValues contentValues = new ContentValues();
-        contentValues.put(Data.MIMETYPE, mimeType);
-        contentValues.put(CUSTOM_DATA_COLUMN, value);
-        newContactValues.add(contentValues);
-    }
-
-    private void getMozillaContactFlagValues(List<ContentValues> newContactValues) {
-        try {
-            JSONArray mozillaContactsFlag = new JSONArray();
-            mozillaContactsFlag.put("1");
-            getGenericValues(MIMETYPE_MOZILLA_CONTACTS_FLAG, CUSTOM_DATA_COLUMN, mozillaContactsFlag, newContactValues);
-        } catch (JSONException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    private ContentValues createContentValues(final String mimeType, final String value, final int typeConstant,
-                                              final String type, final boolean preferredValue) {
-        ContentValues contentValues = new ContentValues();
-        contentValues.put(Data.MIMETYPE, mimeType);
-        contentValues.put(Data.DATA1, value);
-        contentValues.put(Data.IS_SUPER_PRIMARY, preferredValue ? 1 : 0);
-
-        if (type != null) {
-            contentValues.put(Data.DATA2, typeConstant);
-
-            // If a custom type, add a label
-            if (typeConstant == BaseTypes.TYPE_CUSTOM) {
-                contentValues.put(Data.DATA3, type);
-            }
-        }
-
-        return contentValues;
-    }
-
-    private void getContactsCount(final String requestID) {
-        Cursor cursor = getAllRawContactIdsCursor();
-        Integer numContacts = cursor.getCount();
-        cursor.close();
-
-        sendCallbackToJavascript("Android:Contacts:Count", requestID, new String[] {"count"},
-                                 new Object[] {numContacts});
-    }
-
-    private void sendCallbackToJavascript(final String subject, final String requestID,
-                                          final String[] argNames, final Object[] argValues) {
-        // Check the same number of argument names and arguments were given
-        if (argNames != null && argNames.length != argValues.length) {
-            throw new IllegalArgumentException("Argument names and argument values lengths do not match. " +
-                                               "Names length = " + argNames.length + ", Values length = " +
-                                               argValues.length);
-        }
-
-        try {
-            JSONObject callbackMessage = new JSONObject();
-            callbackMessage.put("requestID", requestID);
-
-            if (argNames != null) {
-                for (int i = 0; i < argNames.length; i++) {
-                    callbackMessage.put(argNames[i], argValues[i]);
-                }
-            }
-
-            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(subject, callbackMessage.toString()));
-        } catch (JSONException e) {
-            throw new IllegalArgumentException(e);
-        }
-    }
-
-    private ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) {
-        try {
-            return mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations);
-        } catch (RemoteException e) {
-            Log.e(LOGTAG, "RemoteException", e);
-        } catch (OperationApplicationException e) {
-            Log.e(LOGTAG, "OperationApplicationException", e);
-        }
-        return null;
-    }
-
-    private void getDeviceAccount(final Runnable handleMessage) {
-        Account[] accounts = AccountManager.get(mActivity).getAccounts();
-
-        if (accounts.length == 0) {
-            Log.w(LOGTAG, "No accounts available");
-            gotDeviceAccount(handleMessage);
-        } else if (accounts.length > 1) {
-            // Show the accounts chooser dialog if more than one dialog exists
-            showAccountsDialog(accounts, handleMessage);
-        } else {
-            // If only one account exists, use it
-            mAccountName = accounts[0].name;
-            mAccountType = accounts[0].type;
-            gotDeviceAccount(handleMessage);
-        }
-
-        mGotDeviceAccount = true;
-    }
-
-    private void showAccountsDialog(final Account[] accounts, final Runnable handleMessage) {
-        String[] accountNames = new String[accounts.length];
-        for (int i = 0; i < accounts.length; i++) {
-            accountNames[i] = accounts[i].name;
-        }
-
-        final AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
-        builder.setTitle(mActivity.getResources().getString(R.string.contacts_account_chooser_dialog_title2))
-            .setSingleChoiceItems(accountNames, 0, new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int position) {
-                    // Set the account name and type when an item is selected and dismiss the dialog
-                    mAccountName = accounts[position].name;
-                    mAccountType = accounts[position].type;
-                    dialog.dismiss();
-                    gotDeviceAccount(handleMessage);
-                }
-            });
-
-        mActivity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                builder.show();
-            }
-        });
-    }
-
-    private void gotDeviceAccount(final Runnable handleMessage) {
-        // Force the handleMessage runnable and getDefaultGroupId to run on the background thread
-        Runnable runnable = new Runnable() {
-            @Override
-            public void run() {
-                getDefaultGroupId();
-
-                // Don't log a user's account if not debug mode. Otherwise, just log a message
-                // saying that we got an account to use
-                if (mAccountName == null) {
-                    Log.i(LOGTAG, "No device account selected. Leaving account as null.");
-                } else if (DEBUG) {
-                    Log.d(LOGTAG, "Using account: " + mAccountName + " (type: " + mAccountType + ")");
-                } else {
-                    Log.i(LOGTAG, "Got device account to use for contact operations.");
-                }
-                handleMessage.run();
-            }
-        };
-
-        ThreadUtils.postToBackgroundThread(runnable);
-    }
-
-    private void getDefaultGroupId() {
-        Cursor cursor = getAllGroups();
-
-        cursor.moveToPosition(-1);
-        while (cursor.moveToNext()) {
-            // Check if the account name and type for the group match the account name and type of
-            // the account we're working with
-            final String groupAccountName = cursor.getString(GROUP_ACCOUNT_NAME);
-            if (!groupAccountName.equals(mAccountName)) {
-                continue;
-            }
-
-            final String groupAccountType = cursor.getString(GROUP_ACCOUNT_TYPE);
-            if (!groupAccountType.equals(mAccountType)) {
-                continue;
-            }
-
-            // For all honeycomb and up, the default group is the first one which has the AUTO_ADD flag set
-            if (isAutoAddGroup(cursor)) {
-                mGroupTitle = cursor.getString(GROUP_TITLE);
-                mGroupId = cursor.getLong(GROUP_ID);
-                break;
-            } else if (PRE_HONEYCOMB_DEFAULT_GROUP.equals(cursor.getString(GROUP_TITLE))) {
-                mGroupId = cursor.getLong(GROUP_ID);
-                mGroupTitle = PRE_HONEYCOMB_DEFAULT_GROUP;
-                break;
-            }
-        }
-        cursor.close();
-
-        if (mGroupId == 0) {
-            Log.w(LOGTAG, "Default group ID not found. Newly created contacts will not belong to any groups.");
-        } else if (DEBUG) {
-            Log.i(LOGTAG, "Using group ID: " + mGroupId + " (" + mGroupTitle + ")");
-        }
-    }
-
-    private static boolean isAutoAddGroup(Cursor cursor) {
-        // For Honeycomb and up, the default group is the first one which has the AUTO_ADD flag set.
-        // For everything below Honeycomb, use the default "System Group: My Contacts" group
-        return (Versions.feature11Plus &&
-                !cursor.isNull(GROUP_AUTO_ADD) &&
-                cursor.getInt(GROUP_AUTO_ADD) != 0);
-    }
-
-    private long getGroupId(String groupName) {
-        long groupId = -1;
-        Cursor cursor = getGroups(Groups.TITLE + " = '" + groupName + "'");
-
-        cursor.moveToPosition(-1);
-        while (cursor.moveToNext()) {
-            String groupAccountName = cursor.getString(GROUP_ACCOUNT_NAME);
-            String groupAccountType = cursor.getString(GROUP_ACCOUNT_TYPE);
-
-            // Check if the account name and type for the group match the account name and type of
-            // the account we're working with or the default "Phone" account if no account was found
-            if (groupAccountName.equals(mAccountName) && groupAccountType.equals(mAccountType) ||
-                (mAccountName == null && "Phone".equals(groupAccountType))) {
-                if (groupName.equals(cursor.getString(GROUP_TITLE))) {
-                    groupId = cursor.getLong(GROUP_ID);
-                    break;
-                }
-            }
-        }
-        cursor.close();
-
-        return groupId;
-    }
-
-    private String getGroupName(long groupId) {
-        Cursor cursor = getGroups(Groups._ID + " = " + groupId);
-
-        if (cursor.getCount() == 0) {
-            cursor.close();
-            return null;
-        }
-
-        cursor.moveToPosition(0);
-        String groupName = cursor.getString(cursor.getColumnIndex(Groups.TITLE));
-        cursor.close();
-
-        return groupName;
-    }
-
-    private Cursor getAllGroups() {
-        return getGroups(null);
-    }
-
-    private Cursor getGroups(String selectArg) {
-        String[] columns = new String[] {
-            Groups.ACCOUNT_NAME,
-            Groups.ACCOUNT_TYPE,
-            Groups._ID,
-            Groups.TITLE,
-            (Versions.feature11Plus ? Groups.AUTO_ADD : Groups._ID)
-        };
-
-        if (selectArg != null) {
-            selectArg = "AND " + selectArg;
-        } else {
-            selectArg = "";
-        }
-
-        return mContentResolver.query(Groups.CONTENT_URI, columns,
-                                      Groups.ACCOUNT_TYPE + " NOT NULL AND " +
-                                      Groups.ACCOUNT_NAME + " NOT NULL " + selectArg, null, null);
-    }
-
-    private long createGroup(String groupName) {
-        if (DEBUG) {
-            Log.d(LOGTAG, "Creating group: " + groupName);
-        }
-
-        ArrayList<ContentProviderOperation> newGroupOptions = new ArrayList<ContentProviderOperation>();
-
-        // Create the group under the account we're using
-        // If no account is selected, use a default account name/type for the group
-        newGroupOptions.add(ContentProviderOperation.newInsert(Groups.CONTENT_URI)
-                                .withValue(Groups.ACCOUNT_NAME, (mAccountName == null ? "Phone" : mAccountName))
-                                .withValue(Groups.ACCOUNT_TYPE, (mAccountType == null ? "Phone" : mAccountType))
-                                .withValue(Groups.TITLE, groupName)
-                                .withValue(Groups.GROUP_VISIBLE, true)
-                                .build());
-
-        applyBatch(newGroupOptions);
-
-        // Return the ID of the newly created group
-        return getGroupId(groupName);
-    }
-
-    private long[] getAllRawContactIds() {
-        Cursor cursor = getAllRawContactIdsCursor();
-
-        // Put the ids into an array
-        long[] ids = new long[cursor.getCount()];
-        int index = 0;
-        cursor.moveToPosition(-1);
-        while(cursor.moveToNext()) {
-            ids[index] = cursor.getLong(cursor.getColumnIndex(RawContacts._ID));
-            index++;
-        }
-        cursor.close();
-
-        return ids;
-    }
-
-    private Cursor getAllRawContactIdsCursor() {
-        // When a contact is deleted, it actually just sets the deleted field to 1 until the
-        // sync adapter actually deletes the contact later so ignore any contacts with the deleted
-        // flag set
-        String selection = RawContacts.DELETED + "=0";
-        String[] selectionArgs = null;
-
-        // Only get contacts from the selected account
-        if (mAccountName != null) {
-            selection += " AND " + RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=?";
-            selectionArgs = new String[] {mAccountName, mAccountType};
-        }
-
-        // Get the ID's of all contacts and use the number of contact ID's as
-        // the total number of contacts
-        return mContentResolver.query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID},
-                                      selection, selectionArgs, null);
-    }
-
-    private static Long getRawContactIdFromContentProviderResults(ContentProviderResult[] results) throws NumberFormatException {
-        for (int i = 0; i < results.length; i++) {
-            if (results[i].uri == null) {
-                continue;
-            }
-
-            String uri = results[i].uri.toString();
-            // Check if the uri is from the raw contacts table
-            if (uri.contains("raw_contacts")) {
-                // The ID is the after the final forward slash in the URI
-                return Long.parseLong(uri.substring(uri.lastIndexOf("/") + 1));
-            }
-        }
-
-        return null;
-    }
-
-    private static boolean checkForPositiveCountInResults(ContentProviderResult[] results) {
-        for (int i = 0; i < results.length; i++) {
-            Integer count = results[i].count;
-
-            if (DEBUG) {
-                Log.d(LOGTAG, "Results count: " + count);
-            }
-
-            if (count != null && count > 0) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    private static long[] convertLongListToArray(List<Long> list) {
-        long[] array = new long[list.size()];
-
-        for (int i = 0; i < list.size(); i++) {
-            array[i] = list.get(i);
-        }
-
-        return array;
-    }
-
-    private static boolean doesJSONArrayContainString(final JSONArray array, final String value) {
-        for (int i = 0; i < array.length(); i++) {
-            if (value.equals(array.optString(i))) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    private static int max(int... values) {
-        int max = values[0];
-        for (int value : values) {
-            if (value > max) {
-                max = value;
-            }
-        }
-        return max;
-    }
-
-    private static void putPossibleNullValueInJSONObject(final String key, final Object value, JSONObject jsonObject) throws JSONException{
-        if (value != null) {
-            jsonObject.put(key, value);
-        } else {
-            jsonObject.put(key, JSONObject.NULL);
-        }
-    }
-
-    private static String getKeyFromMapValue(final HashMap<String, Integer> map, int value) {
-        for (Entry<String, Integer> entry : map.entrySet()) {
-            if (value == entry.getValue()) {
-                return entry.getKey();
-            }
-        }
-        return null;
-    }
-
-    private String getColumnNameConstant(String field) {
-        initColumnNameConstantsMap();
-        return mColumnNameConstantsMap.get(field.toLowerCase());
-    }
-
-    private void initColumnNameConstantsMap() {
-        if (mColumnNameConstantsMap != null) {
-            return;
-        }
-        mColumnNameConstantsMap = new HashMap<String, String>();
-
-        mColumnNameConstantsMap.put("name", StructuredName.DISPLAY_NAME);
-        mColumnNameConstantsMap.put("givenname", StructuredName.GIVEN_NAME);
-        mColumnNameConstantsMap.put("familyname", StructuredName.FAMILY_NAME);
-        mColumnNameConstantsMap.put("honorificprefix", StructuredName.PREFIX);
-        mColumnNameConstantsMap.put("honorificsuffix", StructuredName.SUFFIX);
-        mColumnNameConstantsMap.put("additionalname", CUSTOM_DATA_COLUMN);
-        mColumnNameConstantsMap.put("nickname", Nickname.NAME);
-        mColumnNameConstantsMap.put("adr", StructuredPostal.STREET);
-        mColumnNameConstantsMap.put("email", Email.ADDRESS);
-        mColumnNameConstantsMap.put("url", Website.URL);
-        mColumnNameConstantsMap.put("category", GroupMembership.GROUP_ROW_ID);
-        mColumnNameConstantsMap.put("tel", Phone.NUMBER);
-        mColumnNameConstantsMap.put("org", Organization.COMPANY);
-        mColumnNameConstantsMap.put("jobTitle", Organization.TITLE);
-        mColumnNameConstantsMap.put("note", Note.NOTE);
-        mColumnNameConstantsMap.put("impp", Im.DATA);
-        mColumnNameConstantsMap.put("sex", CUSTOM_DATA_COLUMN);
-        mColumnNameConstantsMap.put("genderidentity", CUSTOM_DATA_COLUMN);
-        mColumnNameConstantsMap.put("key", CUSTOM_DATA_COLUMN);
-    }
-
-    private String getMimeTypeOfField(String field) {
-        initMimeTypeConstantsMap();
-        return mMimeTypeConstantsMap.get(field.toLowerCase());
-    }
-
-    private void initMimeTypeConstantsMap() {
-        if (mMimeTypeConstantsMap != null) {
-            return;
-        }
-        mMimeTypeConstantsMap = new HashMap<String, String>();
-
-        mMimeTypeConstantsMap.put("name", StructuredName.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("givenname", StructuredName.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("familyname", StructuredName.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("honorificprefix", StructuredName.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("honorificsuffix", StructuredName.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("additionalname", MIMETYPE_ADDITIONAL_NAME);
-        mMimeTypeConstantsMap.put("nickname", Nickname.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("email", Email.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("url", Website.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("category", GroupMembership.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("tel", Phone.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("org", Organization.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("jobTitle", Organization.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("note", Note.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("impp", Im.CONTENT_ITEM_TYPE);
-        mMimeTypeConstantsMap.put("sex", MIMETYPE_SEX);
-        mMimeTypeConstantsMap.put("genderidentity", MIMETYPE_GENDER_IDENTITY);
-        mMimeTypeConstantsMap.put("key", MIMETYPE_KEY);
-    }
-
-    private int getAddressType(String addressType) {
-        initAddressTypesMap();
-        Integer type = mAddressTypesMap.get(addressType.toLowerCase());
-        return type != null ? type : StructuredPostal.TYPE_CUSTOM;
-    }
-
-    private void initAddressTypesMap() {
-        if (mAddressTypesMap != null) {
-            return;
-        }
-        mAddressTypesMap = new HashMap<String, Integer>();
-
-        mAddressTypesMap.put("home", StructuredPostal.TYPE_HOME);
-        mAddressTypesMap.put("work", StructuredPostal.TYPE_WORK);
-    }
-
-    private int getPhoneType(String phoneType) {
-        initPhoneTypesMap();
-        Integer type = mPhoneTypesMap.get(phoneType.toLowerCase());
-        return type != null ? type : Phone.TYPE_CUSTOM;
-    }
-
-    private void initPhoneTypesMap() {
-        if (mPhoneTypesMap != null) {
-            return;
-        }
-        mPhoneTypesMap = new HashMap<String, Integer>();
-
-        mPhoneTypesMap.put("home", Phone.TYPE_HOME);
-        mPhoneTypesMap.put("mobile", Phone.TYPE_MOBILE);
-        mPhoneTypesMap.put("work", Phone.TYPE_WORK);
-        mPhoneTypesMap.put("fax home", Phone.TYPE_FAX_HOME);
-        mPhoneTypesMap.put("fax work", Phone.TYPE_FAX_WORK);
-        mPhoneTypesMap.put("pager", Phone.TYPE_PAGER);
-        mPhoneTypesMap.put("callback", Phone.TYPE_CALLBACK);
-        mPhoneTypesMap.put("car", Phone.TYPE_CAR);
-        mPhoneTypesMap.put("company main", Phone.TYPE_COMPANY_MAIN);
-        mPhoneTypesMap.put("isdn", Phone.TYPE_ISDN);
-        mPhoneTypesMap.put("main", Phone.TYPE_MAIN);
-        mPhoneTypesMap.put("fax other", Phone.TYPE_OTHER_FAX);
-        mPhoneTypesMap.put("other fax", Phone.TYPE_OTHER_FAX);
-        mPhoneTypesMap.put("radio", Phone.TYPE_RADIO);
-        mPhoneTypesMap.put("telex", Phone.TYPE_TELEX);
-        mPhoneTypesMap.put("tty", Phone.TYPE_TTY_TDD);
-        mPhoneTypesMap.put("ttd", Phone.TYPE_TTY_TDD);
-        mPhoneTypesMap.put("work mobile", Phone.TYPE_WORK_MOBILE);
-        mPhoneTypesMap.put("work pager", Phone.TYPE_WORK_PAGER);
-        mPhoneTypesMap.put("assistant", Phone.TYPE_ASSISTANT);
-        mPhoneTypesMap.put("mms", Phone.TYPE_MMS);
-    }
-
-    private int getEmailType(String emailType) {
-        initEmailTypesMap();
-        Integer type = mEmailTypesMap.get(emailType.toLowerCase());
-        return type != null ? type : Email.TYPE_CUSTOM;
-    }
-
-    private void initEmailTypesMap() {
-        if (mEmailTypesMap != null) {
-            return;
-        }
-        mEmailTypesMap = new HashMap<String, Integer>();
-
-        mEmailTypesMap.put("home", Email.TYPE_HOME);
-        mEmailTypesMap.put("mobile", Email.TYPE_MOBILE);
-        mEmailTypesMap.put("work", Email.TYPE_WORK);
-    }
-
-    private int getWebsiteType(String websiteType) {
-        initWebsiteTypesMap();
-        Integer type = mWebsiteTypesMap.get(websiteType.toLowerCase());
-        return type != null ? type : Website.TYPE_CUSTOM;
-    }
-
-    private void initWebsiteTypesMap() {
-        if (mWebsiteTypesMap != null) {
-            return;
-        }
-        mWebsiteTypesMap = new HashMap<String, Integer>();
-
-        mWebsiteTypesMap.put("homepage", Website.TYPE_HOMEPAGE);
-        mWebsiteTypesMap.put("blog", Website.TYPE_BLOG);
-        mWebsiteTypesMap.put("profile", Website.TYPE_PROFILE);
-        mWebsiteTypesMap.put("home", Website.TYPE_HOME);
-        mWebsiteTypesMap.put("work", Website.TYPE_WORK);
-        mWebsiteTypesMap.put("ftp", Website.TYPE_FTP);
-    }
-
-    private int getImType(String imType) {
-        initImTypesMap();
-        Integer type = mImTypesMap.get(imType.toLowerCase());
-        return type != null ? type : Im.TYPE_CUSTOM;
-    }
-
-    private void initImTypesMap() {
-        if (mImTypesMap != null) {
-            return;
-        }
-        mImTypesMap = new HashMap<String, Integer>();
-
-        mImTypesMap.put("home", Im.TYPE_HOME);
-        mImTypesMap.put("work", Im.TYPE_WORK);
-    }
-
-    private String[] getAllColumns() {
-        return new String[] {Entity.DATA_ID, Data.MIMETYPE, Data.IS_SUPER_PRIMARY,
-                             Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4,
-                             Data.DATA5, Data.DATA6, Data.DATA7, Data.DATA8,
-                             Data.DATA9, Data.DATA10, Data.DATA11, Data.DATA12,
-                             Data.DATA13, Data.DATA14, Data.DATA15};
-    }
-}
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -63,17 +63,16 @@ import android.location.Location;
 import android.location.LocationListener;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.StrictMode;
-import android.provider.ContactsContract;
 import android.provider.MediaStore.Images.Media;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Base64;
 import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -171,17 +170,16 @@ public abstract class GeckoApp
     private View mCameraView;
     private OrientationEventListener mCameraOrientationEventListener;
     public List<GeckoAppShell.AppStateListener> mAppStateListeners = new LinkedList<GeckoAppShell.AppStateListener>();
     protected MenuPanel mMenuPanel;
     protected Menu mMenu;
     protected GeckoProfile mProfile;
     protected boolean mIsRestoringActivity;
 
-    private ContactService mContactService;
     private PromptService mPromptService;
     private TextSelection mTextSelection;
 
     protected DoorHangerPopup mDoorHangerPopup;
     protected FormAssistPopup mFormAssistPopup;
     protected ButtonToast mToast;
 
     protected LayerView mLayerView;
@@ -572,32 +570,16 @@ public abstract class GeckoApp
                         @Override
                         public void run() {
                             Toast.makeText(context, resId, Toast.LENGTH_SHORT).show();
                         }
                     });
                 }
             });
 
-        } else if ("Contact:Add".equals(event)) {
-            final String email = message.optString("email", null);
-            final String phone = message.optString("phone", null);
-            if (email != null) {
-                Uri contactUri = Uri.parse(email);
-                Intent i = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, contactUri);
-                startActivity(i);
-            } else if (phone != null) {
-                Uri contactUri = Uri.parse(phone);
-                Intent i = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, contactUri);
-                startActivity(i);
-            } else {
-                // something went wrong.
-                Log.e(LOGTAG, "Received Contact:Add message with no email nor phone number");
-            }
-
         } else if ("DevToolsAuth:Scan".equals(event)) {
             DevToolsAuthHelper.scan(this, callback);
 
         } else if ("DOMFullScreen:Start".equals(event)) {
             // Local ref to layerView for thread safety
             LayerView layerView = mLayerView;
             if (layerView != null) {
                 layerView.setFullScreenState(message.getBoolean("rootElement")
@@ -1232,17 +1214,16 @@ public abstract class GeckoApp
             "Gecko:DelayedStartup",
             "Gecko:Exited",
             "Accessibility:Event",
             "NativeApp:IsDebuggable");
 
         EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this,
             "Accessibility:Ready",
             "Bookmark:Insert",
-            "Contact:Add",
             "DevToolsAuth:Scan",
             "DOMFullScreen:Start",
             "DOMFullScreen:Stop",
             "Image:SetAs",
             "Locale:Set",
             "Permissions:Data",
             "PrivateBrowsing:Data",
             "Session:StatePurged",
@@ -1553,18 +1534,16 @@ public abstract class GeckoApp
 
         //app state callbacks
         mAppStateListeners = new LinkedList<GeckoAppShell.AppStateListener>();
 
         if (SmsManager.isEnabled()) {
             SmsManager.getInstance().start();
         }
 
-        mContactService = new ContactService(EventDispatcher.getInstance(), this);
-
         mPromptService = new PromptService(this);
 
         mTextSelection = new TextSelection((TextSelectionHandle) findViewById(R.id.anchor_handle),
                                            (TextSelectionHandle) findViewById(R.id.caret_handle),
                                            (TextSelectionHandle) findViewById(R.id.focus_handle));
 
         // Trigger the completion of the telemetry timer that wraps activity startup,
         // then grab the duration to give to FHR.
@@ -2044,17 +2023,16 @@ public abstract class GeckoApp
             "Gecko:DelayedStartup",
             "Gecko:Exited",
             "Accessibility:Event",
             "NativeApp:IsDebuggable");
 
         EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener)this,
             "Accessibility:Ready",
             "Bookmark:Insert",
-            "Contact:Add",
             "DOMFullScreen:Start",
             "DOMFullScreen:Stop",
             "Image:SetAs",
             "Locale:Set",
             "Permissions:Data",
             "PrivateBrowsing:Data",
             "Session:StatePurged",
             "Share:Text",
@@ -2075,18 +2053,16 @@ public abstract class GeckoApp
         deleteTempFiles();
 
         if (mLayerView != null)
             mLayerView.destroy();
         if (mDoorHangerPopup != null)
             mDoorHangerPopup.destroy();
         if (mFormAssistPopup != null)
             mFormAssistPopup.destroy();
-        if (mContactService != null)
-            mContactService.destroy();
         if (mPromptService != null)
             mPromptService.destroy();
         if (mTextSelection != null)
             mTextSelection.destroy();
         NotificationHelper.destroy();
         IntentHelper.destroy();
         GeckoNetworkManager.destroy();
 
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -158,17 +158,16 @@ gbjar.sources += [
     'animation/TransitionsTracker.java',
     'animation/ViewHelper.java',
     'ANRReporter.java',
     'AppNotificationClient.java',
     'Assert.java',
     'BaseGeckoInterface.java',
     'BrowserApp.java',
     'BrowserLocaleManager.java',
-    'ContactService.java',
     'ContextGetter.java',
     'CrashHandler.java',
     'CustomEditText.java',
     'DataReportingNotification.java',
     'db/AbstractPerProfileDatabaseProvider.java',
     'db/AbstractTransactionalProvider.java',
     'db/BaseTable.java',
     'db/BrowserContract.java',
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -518,20 +518,16 @@
 
   <string name="suggestion_for_engine">&suggestion_for_engine;</string>
 
   <!-- Set Image Notifications -->
   <string name="set_image_fail">&set_image_fail;</string>
   <string name="set_image_path_fail">&set_image_path_fail;</string>
   <string name="set_image_chooser_title">&set_image_chooser_title;</string>
 
-  <!-- Contacts API -->
-  <string name="contacts_account_chooser_dialog_title2">Share contacts from…</string>
-
-
   <!-- Guest mode -->
   <string name="new_guest_session">&new_guest_session;</string>
   <string name="exit_guest_session">&exit_guest_session;</string>
   <string name="guest_session_dialog_continue">&guest_session_dialog_continue;</string>
   <string name="guest_session_dialog_cancel">&guest_session_dialog_cancel;</string>
   <string name="new_guest_session_title">&new_guest_session_title;</string>
   <string name="new_guest_session_text">&new_guest_session_text2;</string>
   <string name="guest_browsing_notification_title">&guest_browsing_notification_title;</string>
--- a/mobile/android/chrome/content/WebappRT.js
+++ b/mobile/android/chrome/content/WebappRT.js
@@ -7,17 +7,16 @@
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
-Cu.import("resource://gre/modules/ContactService.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm");
 
 function pref(name, value) {
   return {
     name: name,
     value: value
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -815,40 +815,16 @@ var BrowserApp = {
         };
       },
       icon: "drawable://ic_menu_share",
       callback: function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_phone");
       }
     });
 
-    NativeWindow.contextmenus.add(stringGetter("contextmenu.addToContacts"),
-      NativeWindow.contextmenus._disableRestricted("ADD_CONTACT", NativeWindow.contextmenus.emailLinkContext),
-      function(aTarget) {
-        UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_email");
-
-        let url = NativeWindow.contextmenus._getLinkURL(aTarget);
-        Messaging.sendRequest({
-          type: "Contact:Add",
-          email: url
-        });
-      });
-
-    NativeWindow.contextmenus.add(stringGetter("contextmenu.addToContacts"),
-      NativeWindow.contextmenus._disableRestricted("ADD_CONTACT", NativeWindow.contextmenus.phoneNumberLinkContext),
-      function(aTarget) {
-        UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_phone");
-
-        let url = NativeWindow.contextmenus._getLinkURL(aTarget);
-        Messaging.sendRequest({
-          type: "Contact:Add",
-          phone: url
-        });
-      });
-
     NativeWindow.contextmenus.add(stringGetter("contextmenu.bookmarkLink"),
       NativeWindow.contextmenus._disableRestricted("BOOKMARK", NativeWindow.contextmenus.linkBookmarkableContext),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_bookmark");
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         let title = aTarget.textContent || aTarget.title || url;
         Messaging.sendRequest({
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -1,13 +1,13 @@
 ; This Source Code Form is subject to the terms of the Mozilla Public
 ; License, v. 2.0. If a copy of the MPL was not distributed with this
 ; file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-; Package file for the Fennec build. 
+; Package file for the Fennec build.
 ;
 ; File format:
 ;
 ; [] designates a toplevel component. Example: [xpcom]
 ; - in front of a file specifies it to be removed from the destination
 ; * wildcard support to recursively copy the entire directory
 ; ; file comment
 ;
@@ -520,17 +520,17 @@
 @BINPATH@/@PREF_DIR@/mobile-branding.js
 @BINPATH@/@PREF_DIR@/channel-prefs.js
 @BINPATH@/ua-update.json
 @BINPATH@/greprefs.js
 @BINPATH@/defaults/autoconfig/prefcalls.js
 @BINPATH@/defaults/profile/prefs.js
 
 ; [Layout Engine Resources]
-; Style Sheets, Graphics and other Resources used by the layout engine. 
+; Style Sheets, Graphics and other Resources used by the layout engine.
 @BINPATH@/res/EditorOverride.css
 @BINPATH@/res/contenteditable.css
 @BINPATH@/res/designmode.css
 @BINPATH@/res/TopLevelImageDocument.css
 @BINPATH@/res/TopLevelVideoDocument.css
 @BINPATH@/res/table-add-column-after-active.gif
 @BINPATH@/res/table-add-column-after-hover.gif
 @BINPATH@/res/table-add-column-after.gif
@@ -651,17 +651,17 @@ bin/libfreebl_32int64_3.so
 
 #ifdef MOZ_SAFE_BROWSING
 @BINPATH@/components/SafeBrowsing.jsm
 #endif
 @BINPATH@/components/XPIDialogService.js
 @BINPATH@/components/browsercomps.xpt
 
 #ifdef ENABLE_MARIONETTE
-@BINPATH@/chrome/marionette@JAREXT@ 
+@BINPATH@/chrome/marionette@JAREXT@
 @BINPATH@/chrome/marionette.manifest
 @BINPATH@/components/MarionetteComponents.manifest
 @BINPATH@/components/marionettecomponent.js
 #endif
 
 @BINPATH@/components/WebappsUpdateTimer.js
 @BINPATH@/components/DataStore.manifest
 @BINPATH@/components/DataStoreImpl.js
--- a/mobile/android/locales/en-US/chrome/browser.properties
+++ b/mobile/android/locales/en-US/chrome/browser.properties
@@ -123,24 +123,16 @@ desktopNotification.dontAllow=Don't allo
 desktopNotification.ask=Allow %S to use notifications?
 # LOCALIZATION NOTE (desktopNotification.useNotifications): Label that will be
 # used in site settings dialog.
 desktopNotification.useNotifications=Use Notifications
 # LOCALIZATION NOTE (desktopNotification.dontAskAgain): This label appears next to a
 # checkbox to indicate whether or not the user wants to make a permanent decision.
 desktopNotification.dontAskAgain=Don't ask again for this site
 
-# Contacts API
-contacts.allow=Allow
-contacts.dontAllow=Don't allow
-contacts.ask=Allow %S to access your contacts?
-# LOCALIZATION NOTE (contacts.dontAskAgain): This label appears next to a
-# checkbox to indicate whether or not the user wants to make a permanent decision.
-contacts.dontAskAgain=Don't ask again for this site
-
 # Device Storage API
 deviceStorageMusic.allow=Allow
 deviceStorageMusic.dontAllow=Don't allow
 deviceStorageMusic.ask=Allow %S access to your music?
 # LOCALIZATION NOTE (deviceStorageMusic.dontAskAgain): This label appears next to a
 # checkbox to indicate whether or not the user wants to make a permanent decision.
 deviceStorageMusic.dontAskAgain=Don't ask again for this site
 
deleted file mode 100644
--- a/mobile/android/modules/ContactService.jsm
+++ /dev/null
@@ -1,262 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const DEBUG = false;
-function debug(s) { dump("-*- Android ContactService component: " + s + "\n"); }
-
-const Cu = Components.utils;
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-this.EXPORTED_SYMBOLS = [];
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1",
-                                   "nsIMessageListenerManager");
-
-let ContactService = {
-  init: function() {
-    if (DEBUG) debug("Init");
-    this._requestMessages = {};
-
-    // Add listeners for all messages from ContactManager.js
-    let messages = ["Contacts:Clear", "Contacts:Find", "Contacts:GetAll",
-                    "Contacts:GetAll:SendNow", "Contacts:GetCount", "Contacts:GetRevision",
-                    "Contact:Remove", "Contact:Save",];
-    messages.forEach(function(msgName) {
-      ppmm.addMessageListener(msgName, this);
-    }.bind(this));
-
-    // Add listeners for all messages from ContactService.java
-    let returnMessages = ["Android:Contacts:Count",
-                          "Android:Contacts:Clear:Return:OK", "Android:Contacts:Clear:Return:KO",
-                          "Android:Contacts:Find:Return:OK", "Android:Contacts:Find:Return:KO",
-                          "Android:Contacts:GetAll:Next", "Android:Contacts:RegisterForMessages",
-                          "Android:Contact:Remove:Return:OK", "Android:Contact:Remove:Return:KO",
-                          "Android:Contact:Save:Return:OK", "Android:Contact:Save:Return:KO",];
-
-    returnMessages.forEach(function(msgName) {
-      Services.obs.addObserver(this, msgName, false);
-    }.bind(this));
-  },
-
-  _sendMessageToJava: function(aMsg) {
-    Services.androidBridge.handleGeckoMessage(aMsg);
-  },
-
-  _sendReturnMessage: function(aTopic, aRequestID, aResult) {
-    this._requestMessages[aRequestID].target.sendAsyncMessage(aTopic, aResult);
-  },
-
-  _sendAndDeleteReturnMessage: function(aTopic, aRequestID, aResult) {
-    this._sendReturnMessage(aTopic, aRequestID, aResult)
-    delete this._requestMessages[aRequestID];
-  },
-
-  observe: function(aSubject, aTopic, aData) {
-    if (DEBUG) {
-      debug("observe: subject: " + aSubject + " topic: " + aTopic + " data: " + aData);
-    }
-
-    let message = JSON.parse(aData, function date_reviver(k, v) {
-      // The Java service sends dates as strings, so convert them to Dates before
-      // sending them back to the child.
-      if (v != null && v != "null" &&
-          ["updated", "published", "anniversary", "bday"].indexOf(k) != -1) {
-        return new Date(v);
-      }
-      return v;
-    });
-    let requestID = message.requestID;
-
-    // The return message topic is the same as the current topic, but without the "Android:" prefix
-    let returnMessageTopic = aTopic.substring(8);
-
-    switch (aTopic) {
-      case "Android:Contacts:Find:Return:OK":
-        this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contacts: message.contacts});
-        break;
-
-      case "Android:Contacts:Find:Return:KO":
-        this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID});
-        break;
-
-      case "Android:Contact:Save:Return:OK":
-        this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contactID: message.contactID});
-        this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {contactID: message.contactID, reason: message.reason});
-        break;
-
-      case "Android:Contact:Save:Return:KO":
-        this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID});
-        break;
-
-      case "Android:Contact:Remove:Return:OK":
-        this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID, contactID: message.contactID});
-        this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {contactID: message.contactID, reason: "remove"});
-        break;
-
-      case "Android:Contact:Remove:Return:KO":
-        this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID});
-        break;
-
-      case "Android:Contacts:Clear:Return:OK":
-        this._sendReturnMessage(returnMessageTopic, requestID, {requestID: requestID});
-        this._sendAndDeleteReturnMessage("Contact:Changed", requestID, {reason: "remove"});
-        break;
-
-      case "Android:Contact:Clear:Return:KO":
-        this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID});
-        break;
-
-      case "Android:Contacts:GetAll:Next":
-        // GetAll uses a cursor ID instead of a request ID. Translate the request ID back to the cursor ID
-        this._sendReturnMessage(returnMessageTopic, requestID, {cursorId: requestID, contacts: message.contacts});
-
-        // Send a message with no contacts to denote the end of contacts returned by the query
-        this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {cursorId: requestID});
-        break;
-
-      case "Android:Contacts:Count":
-        this._sendAndDeleteReturnMessage(returnMessageTopic, requestID, {requestID: requestID, count: message.count});
-        break;
-
-      default:
-        throw "Wrong message received: " + aTopic;
-    }
-  },
-
-  assertPermission: function(aMessage, aPerm) {
-    if (!aMessage.target.assertPermission(aPerm)) {
-      Cu.reportError("Contacts message " + aMessage.name +
-                     " from a content process with no" + aPerm + " privileges.");
-      return false;
-    }
-    return true;
-  },
-
-  receiveMessage: function(aMessage) {
-    if (DEBUG) debug("receiveMessage " + aMessage.name);
-
-    // GetAll uses a cursor ID instead of a request ID, but they can be treated the same from here
-    if (!aMessage.data.requestID && aMessage.data.cursorId) {
-      aMessage.data.requestID = aMessage.data.cursorId;
-    }
-    let requestID = aMessage.data.requestID;
-
-    // Store the message so it the request callback can be called when the Java side is finished
-    this._requestMessages[requestID] = aMessage;
-
-    switch (aMessage.name) {
-      case "Contacts:Find":
-        this.findContacts(aMessage);
-        break;
-
-      case "Contacts:GetAll":
-        this.getAllContacts(aMessage);
-        break;
-
-      case "Contacts:GetAll:SendNow":
-        // Send an empty message to denote there are no most contacts for the getAll query
-        this._sendAndDeleteReturnMessage("Contacts:GetAll:Next", requestID, {cursorId: requestID});
-        break;
-
-      case "Contact:Save":
-        this.saveContact(aMessage);
-        break;
-
-      case "Contact:Remove":
-        this.removeContact(aMessage);
-        break;
-
-      case "Contacts:Clear":
-        this.clearContacts(aMessage);
-        break;
-
-      case "Contacts:GetCount":
-        this.getContactsCount(aMessage);
-        break;
-
-      case "Contacts:GetRevision":
-        // Android does not support the get revision function
-        this._sendAndDeleteReturnMessage("Contacts:GetRevision:Return:KO", requestID, {requestID: requestID,
-                                          errorMsg: "Android does not support the revision function."});
-        break;
-
-      case "Contacts:RegisterForMessages":
-        delete this._requestMessages[requestID];
-        break;
-
-      default:
-        delete this._requestMessages[requestID];
-        throw "Wrong message received: " + aMessage.name;
-    }
-  },
-
-  findContacts: function(aMessage) {
-    if (!this.assertPermission(aMessage, "contacts-read")) {
-      return;
-    }
-
-    let countryName = PhoneNumberUtils.getCountryName();
-    let substringmatchingPref = "dom.phonenumber.substringmatching." + countryName;
-    let substringmatchingValue = 0;
-    if (Services.prefs.getPrefType(substringmatchingPref) == Ci.nsIPrefBranch.PREF_INT) {
-      substringmatchingValue = Services.prefs.getIntPref(substringmatchingPref);
-    }
-
-    // Add the substring matching value to the find options JSON
-    aMessage.data.options.findOptions.substringMatching = substringmatchingValue;
-
-    this._sendMessageToJava({type: "Android:Contacts:Find", data: aMessage.data});
-  },
-
-  getAllContacts: function(aMessage) {
-    if (!this.assertPermission(aMessage, "contacts-read")) {
-      return;
-    }
-
-    this._sendMessageToJava({type: "Android:Contacts:GetAll", data: aMessage.data});
-  },
-
-  saveContact: function(aMessage) {
-    if ((aMessage.data.options.reason === "create" &&
-        !this.assertPermission(aMessage, "contacts-create")) ||
-        !this.assertPermission(aMessage, "contacts-write")) {
-        return;
-    }
-
-    this._sendMessageToJava({type: "Android:Contact:Save", data: aMessage.data});
-  },
-
-  removeContact: function(aMessage) {
-    if (!this.assertPermission(aMessage, "contacts-write")) {
-      return;
-    }
-
-    this._sendMessageToJava({type: "Android:Contact:Remove", data: aMessage.data});
-  },
-
-  clearContacts: function(aMessage) {
-    if (!this.assertPermission(aMessage, "contacts-write")) {
-      return;
-    }
-
-    this._sendMessageToJava({type: "Android:Contacts:Clear", data: aMessage.data});
-  },
-
-  getContactsCount: function(aMessage) {
-    if (!this.assertPermission(aMessage, "contacts-read")) {
-      return;
-    }
-
-    this._sendMessageToJava({type: "Android:Contacts:GetCount", data: aMessage.data});
-  },
-}
-
-ContactService.init();
--- a/mobile/android/modules/moz.build
+++ b/mobile/android/modules/moz.build
@@ -2,17 +2,16 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXTRA_JS_MODULES += [
     'Accounts.jsm',
     'AndroidLog.jsm',
-    'ContactService.jsm',
     'dbg-browser-actors.js',
     'DelayedInit.jsm',
     'DownloadNotifications.jsm',
     'HelperApps.jsm',
     'Home.jsm',
     'HomeProvider.jsm',
     'JavaAddonManager.jsm',
     'JNI.jsm',