blob: 44ab61eb9d1b9649895824409fa03f8ef7c838e3 [file] [log] [blame]
/*
* Copyright (c) 2015, Motorola Mobility LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - Neither the name of Motorola Mobility nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package com.android.service.ims.presence;
import static com.android.service.ims.presence.AccountUtil.ACCOUNT_TYPE;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import com.android.ims.internal.ContactNumberUtils;
import com.android.ims.internal.EABContract;
import com.android.ims.internal.Logger;
public class EABDbUtil {
static private Logger logger = Logger.getLogger("EABDbUtil");
public static boolean validateAndSyncFromContactsDb(Context context) {
logger.debug("Enter validateAndSyncFromContactsDb");
boolean response = true;
// Get the last stored contact changed timestamp and sync only delta contacts.
long contactLastChange = SharedPrefUtil.getLastContactChangedTimestamp(context, 0);
logger.debug("contact last updated time before init :" + contactLastChange);
ContentResolver contentResolver = context.getContentResolver();
String[] projection = new String[] { ContactsContract.Contacts._ID,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP };
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + "> '0' AND "
+ ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP
+ " >'" + contactLastChange + "'";
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " asc";
Cursor cursor = contentResolver.query(Contacts.CONTENT_URI, projection, selection,
null, sortOrder);
ArrayList<PresenceContact> allEligibleContacts = new ArrayList<PresenceContact>();
logger.debug("cursor count : " + cursor.getCount());
if (cursor.moveToFirst()) {
do {
String id = cursor.getString(cursor.getColumnIndex(Contacts._ID));
Long time = cursor.getLong(cursor.getColumnIndex(
Contacts.CONTACT_LAST_UPDATED_TIMESTAMP));
// Update the latest contact last modified timestamp.
if (contactLastChange < time) {
contactLastChange = time;
}
String[] commonDataKindsProjection = new String[] {
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID };
Cursor pCur = contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
commonDataKindsProjection,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ " = ?", new String[] { id }, null);
ArrayList<String> phoneNumList = new ArrayList<String>();
if (pCur.moveToFirst()) {
do {
String contactNumber = pCur.getString(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
//contactNumber = filterEligibleContact(context, pContactNumber);
if (validateEligibleContact(context, contactNumber)) {
String contactName = pCur.getString(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String rawContactId = pCur.getString(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID));
String contactId = pCur.getString(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
// TODO: HACK - To be resolved as part of EAB Provider rework.
if (phoneNumList.contains(contactNumber)) continue;
phoneNumList.add(contactNumber);
String dataId = getDataId(contentResolver,rawContactId, contactNumber);
if (null != dataId) {
allEligibleContacts.add(new PresenceContact(contactName,
contactNumber, rawContactId, contactId, dataId));
} else {
logger.debug("dataId is null. Don't add contact to " +
"allEligibleContacts.");
}
}
} while (pCur.moveToNext());
}
pCur.close();
} while (cursor.moveToNext());
}
if (null != cursor) {
cursor.close();
}
if (allEligibleContacts.size() > 0) {
logger.debug("Adding : " + allEligibleContacts.size() +
" new contact numbers to EAB db.");
addContactsToEabDb(context, allEligibleContacts);
logger.debug("contact last updated time after init :" + contactLastChange);
SharedPrefUtil.saveLastContactChangedTimestamp(context, contactLastChange);
SharedPrefUtil.saveLastContactDeletedTimestamp(context, contactLastChange);
}
logger.debug("Exit validateAndSyncFromContactsDb contact numbers synced : " +
allEligibleContacts.size());
return response;
}
private static String getDataId(ContentResolver contentResolver,
String rawContactId, String pContactNumber) {
String dataId = null;
String where = Data.RAW_CONTACT_ID + " = '" + rawContactId + "' AND "
+ Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'"
+ " AND " + Data.DATA1 + "='" + pContactNumber + "'";
Cursor cur = null;
try {
cur = contentResolver.query(Data.CONTENT_URI,
new String[] { Data._ID }, where, null, null);
if (cur.moveToFirst()) {
dataId = cur.getString(cur.getColumnIndex(Data._ID));
}
} catch (SQLiteException e) {
logger.debug("SQLiteException while querying for dataId : " + e.toString());
} catch (Exception e) {
logger.debug("Exception while querying for dataId : " + e);
} finally {
if (null != cur) {
cur.close();
}
}
return dataId;
}
public static void addContactsToEabDb(Context context,
ArrayList<PresenceContact> contactList) {
ArrayList<ContentProviderOperation> operation = new ArrayList<ContentProviderOperation>();
logger.debug("Adding Contacts to EAB DB");
// To avoid the following exception - Too many content provider operations
// between yield points. The maximum number of operations per yield point is
// 500 for exceuteDB()
int yieldPoint = 300;
for (int j = 0; j < contactList.size(); j++) {
addContactToEabDb(context, operation, contactList.get(j).getDisplayName(),
contactList.get(j).getPhoneNumber(), contactList.get(j).getRawContactId(),
contactList.get(j).getContactId(), contactList.get(j).getDataId());
if (yieldPoint == j) {
exceuteDB(context, operation);
operation = null;
operation = new ArrayList<ContentProviderOperation>();
yieldPoint += 300;
}
}
exceuteDB(context, operation);
}
private static void addContactToEabDb(
Context context, ArrayList<ContentProviderOperation> ops, String displayName,
String phoneNumber, String rawContactId, String contactId,
String dataId) {
ops.add(ContentProviderOperation
.newInsert(EABContract.EABColumns.CONTENT_URI)
.withValue(EABContract.EABColumns.CONTACT_NAME, displayName)
.withValue(EABContract.EABColumns.CONTACT_NUMBER, phoneNumber)
.withValue(EABContract.EABColumns.ACCOUNT_TYPE, ACCOUNT_TYPE)
.withValue(EABContract.EABColumns.RAW_CONTACT_ID, rawContactId)
.withValue(EABContract.EABColumns.CONTACT_ID, contactId)
.withValue(EABContract.EABColumns.DATA_ID, dataId).build());
}
public static void deleteContactsFromEabDb(Context context,
ArrayList<PresenceContact> contactList) {
ArrayList<ContentProviderOperation> operation = new ArrayList<ContentProviderOperation>();
logger.debug("Deleting Contacts from EAB DB");
String[] contactIdList = new String[contactList.size()];
// To avoid the following exception - Too many content provider operations
// between yield points. The maximum number of operations per yield point is
// 500 for exceuteDB()
int yieldPoint = 300;
for (int j = 0; j < contactList.size(); j++) {
contactIdList[j] = contactList.get(j).getContactId().toString();
deleteContactFromEabDb(context, operation, contactIdList[j]);
if (yieldPoint == j) {
exceuteDB(context, operation);
operation = null;
operation = new ArrayList<ContentProviderOperation>();
yieldPoint += 300;
}
}
exceuteDB(context, operation);
}
private static void deleteContactFromEabDb(Context context,
ArrayList<ContentProviderOperation> ops, String contactId) {
// Add operation only if there is an entry in EABProvider table.
String[] eabProjection = new String[] {
EABContract.EABColumns.CONTACT_NUMBER,
EABContract.EABColumns.CONTACT_ID };
String eabWhereClause = null;
if (ContactsContract.Profile.MIN_ID == Long.valueOf(contactId)) {
eabWhereClause = EABContract.EABColumns.CONTACT_ID + " >='" + contactId + "'";
} else {
eabWhereClause = EABContract.EABColumns.CONTACT_ID + " ='" + contactId + "'";
}
Cursor eabDeleteCursor = context.getContentResolver().query(
EABContract.EABColumns.CONTENT_URI, eabProjection,
eabWhereClause, null, null);
if (null != eabDeleteCursor) {
int count = eabDeleteCursor.getCount();
logger.debug("cursor count : " + count);
if (count > 0) {
eabDeleteCursor.moveToNext();
long eabDeleteContactId = eabDeleteCursor.
getLong(eabDeleteCursor.getColumnIndex(EABContract.EABColumns.CONTACT_ID));
logger.debug("eabDeleteContactId : " + eabDeleteContactId);
if (ContactsContract.Profile.MIN_ID == Long.valueOf(contactId)) {
if (ContactsContract.isProfileId(eabDeleteContactId)) {
logger.debug("Deleting Profile contact.");
ops.add(ContentProviderOperation
.newDelete(EABContract.EABColumns.CONTENT_URI)
.withSelection(EABContract.EABColumns.CONTACT_ID + " >= ?",
new String[] { contactId }).build());
} else {
logger.debug("Not a Profile contact. Do nothing.");
}
} else {
ops.add(ContentProviderOperation
.newDelete(EABContract.EABColumns.CONTENT_URI)
.withSelection(EABContract.EABColumns.CONTACT_ID + " = ?",
new String[] { contactId }).build());
}
}
eabDeleteCursor.close();
}
}
public static void deleteNumbersFromEabDb(Context context,
ArrayList<PresenceContact> contactList) {
ArrayList<ContentProviderOperation> operation = new ArrayList<ContentProviderOperation>();
logger.debug("Deleting Number from EAB DB");
String[] rawContactIdList = new String [contactList.size()];
String[] DataIdList = new String [contactList.size()];
// To avoid the following exception - Too many content provider operations
// between yield points. The maximum number of operations per yield point is
// 500 for exceuteDB()
int yieldPoint = 300;
for (int j = 0; j < contactList.size(); j++) {
rawContactIdList[j] = contactList.get(j).getRawContactId();
DataIdList[j] = contactList.get(j).getDataId();
deleteNumberFromEabDb(context, operation, rawContactIdList[j], DataIdList[j]);
if (yieldPoint == j) {
exceuteDB(context, operation);
operation = null;
operation = new ArrayList<ContentProviderOperation>();
yieldPoint += 300;
}
}
exceuteDB(context, operation);
}
private static void deleteNumberFromEabDb(Context context,
ArrayList<ContentProviderOperation> ops, String rawContactId, String dataId) {
// Add operation only if there is an entry in EABProvider table.
String[] eabProjection = new String[] {
EABContract.EABColumns.CONTACT_NUMBER };
String eabWhereClause = EABContract.EABColumns.RAW_CONTACT_ID + " ='" + rawContactId
+ "' AND " + EABContract.EABColumns.DATA_ID + " ='" + dataId + "'";
Cursor eabDeleteCursor = context.getContentResolver().query(
EABContract.EABColumns.CONTENT_URI, eabProjection,
eabWhereClause, null, null);
if (null != eabDeleteCursor) {
int count = eabDeleteCursor.getCount();
logger.debug("Delete number cursor count : " + count);
if (count > 0) {
ops.add(ContentProviderOperation.newDelete(EABContract.EABColumns.CONTENT_URI)
.withSelection(EABContract.EABColumns.RAW_CONTACT_ID + " = ? AND "
+ EABContract.EABColumns.DATA_ID + " = ?",
new String[] { rawContactId, dataId }).build());
}
eabDeleteCursor.close();
}
}
public static void updateNamesInEabDb(Context context,
ArrayList<PresenceContact> contactList) {
ArrayList<ContentProviderOperation> operation = new ArrayList<ContentProviderOperation>();
logger.debug("Update name in EAB DB");
String[] phoneNameList = new String[contactList.size()];
String[] phoneNumberList = new String[contactList.size()];
String[] rawContactIdList = new String [contactList.size()];
String[] dataIdList = new String [contactList.size()];
// To avoid the following exception - Too many content provider operations
// between yield points. The maximum number of operations per yield point is
// 500 for exceuteDB()
int yieldPoint = 300;
for (int j = 0; j < contactList.size(); j++) {
phoneNameList[j] = contactList.get(j).getDisplayName();
phoneNumberList[j] = contactList.get(j).getPhoneNumber();
rawContactIdList[j] = contactList.get(j).getRawContactId();
dataIdList[j] = contactList.get(j).getDataId();
updateNameInEabDb(context, operation, phoneNameList[j],
phoneNumberList[j], rawContactIdList[j], dataIdList[j]);
if (yieldPoint == j) {
exceuteDB(context, operation);
operation = null;
operation = new ArrayList<ContentProviderOperation>();
yieldPoint += 300;
}
}
exceuteDB(context, operation);
}
private static void updateNameInEabDb(Context context,
ArrayList<ContentProviderOperation> ops, String phoneName,
String phoneNumber, String rawContactId, String dataId) {
ContentValues values = new ContentValues();
values.put(EABContract.EABColumns.CONTACT_NAME, phoneName);
String where = EABContract.EABColumns.CONTACT_NUMBER + " = ? AND "
+ EABContract.EABColumns.RAW_CONTACT_ID + " = ? AND "
+ EABContract.EABColumns.DATA_ID + " = ?";
ops.add(ContentProviderOperation
.newUpdate(EABContract.EABColumns.CONTENT_URI)
.withValues(values)
.withSelection(where, new String[] { phoneNumber, rawContactId, dataId }).build());
}
private static void exceuteDB(Context context, ArrayList<ContentProviderOperation> ops) {
if (ops.size() == 0) {
logger.debug("exceuteDB return as operation size is 0.");
return;
}
try {
context.getContentResolver().applyBatch(EABContract.AUTHORITY, ops);
} catch (RemoteException e) {
e.printStackTrace();
} catch (OperationApplicationException e) {
e.printStackTrace();
}
ops.clear();
logger.debug("exceuteDB return with successful operation.");
return;
}
public static boolean validateEligibleContact(Context context, String mdn) {
boolean number = false;
if (null == mdn) {
logger.debug("validateEligibleContact - mdn is null.");
return number;
}
List<String> mdbList = new ArrayList<String>();
mdbList.add(mdn);
ContactNumberUtils mNumberUtils = ContactNumberUtils.getDefault();
mNumberUtils.setContext(context);
int numberType = mNumberUtils.validate(mdbList);
logger.debug("ContactNumberUtils.validate response : " + numberType);
if ( ContactNumberUtils.NUMBER_VALID == numberType) {
number = true;
}
return number;
}
public static String filterEligibleContact(Context context, String mdn) {
String number = null;
if (null == mdn) {
logger.debug("filterEligibleContact - mdn is null.");
return number;
}
logger.debug("Before filterEligibleContact validation : " + mdn);
List<String> mdbList = new ArrayList<String>();
mdbList.add(mdn);
ContactNumberUtils mNumberUtils = ContactNumberUtils.getDefault();
mNumberUtils.setContext(context);
int numberType = mNumberUtils.validate(mdbList);
logger.debug("ContactNumberUtils.validate response : " + numberType);
if ( ContactNumberUtils.NUMBER_VALID == numberType) {
String[] mdnFormatted = mNumberUtils.format(mdbList);
if (mdnFormatted.length > 0 ){
number = mdnFormatted[0];
}
}
return number;
}
}