RecipientIdCache.java revision db8958a358ad2aa4766027893f3d324007ab976c
1package com.android.mms.data; 2 3import java.util.ArrayList; 4import java.util.HashMap; 5import java.util.List; 6import java.util.Map; 7 8import android.content.Context; 9import android.content.ContentValues; 10import android.content.ContentUris; 11import android.database.Cursor; 12import android.net.Uri; 13import android.text.TextUtils; 14import android.util.Log; 15import android.provider.Telephony; 16 17import com.android.mms.LogTag; 18import android.database.sqlite.SqliteWrapper; 19 20public class RecipientIdCache { 21 private static final boolean LOCAL_DEBUG = false; 22 private static final String TAG = "Mms/cache"; 23 24 private static Uri sAllCanonical = 25 Uri.parse("content://mms-sms/canonical-addresses"); 26 27 private static Uri sSingleCanonicalAddressUri = 28 Uri.parse("content://mms-sms/canonical-address"); 29 30 private static RecipientIdCache sInstance; 31 static RecipientIdCache getInstance() { return sInstance; } 32 private final Map<Long, String> mCache; 33 private final Context mContext; 34 35 public static class Entry { 36 public long id; 37 public String number; 38 39 public Entry(long id, String number) { 40 this.id = id; 41 this.number = number; 42 } 43 }; 44 45 static void init(Context context) { 46 sInstance = new RecipientIdCache(context); 47 new Thread(new Runnable() { 48 public void run() { 49 fill(); 50 } 51 }).start(); 52 } 53 54 RecipientIdCache(Context context) { 55 mCache = new HashMap<Long, String>(); 56 mContext = context; 57 } 58 59 public static void fill() { 60 if (Log.isLoggable(LogTag.THREAD_CACHE, Log.VERBOSE)) { 61 LogTag.debug("[RecipientIdCache] fill: begin"); 62 } 63 64 Context context = sInstance.mContext; 65 Cursor c = SqliteWrapper.query(context, context.getContentResolver(), 66 sAllCanonical, null, null, null, null); 67 if (c == null) { 68 Log.w(TAG, "null Cursor in fill()"); 69 return; 70 } 71 72 try { 73 synchronized (sInstance) { 74 // Technically we don't have to clear this because the stupid 75 // canonical_addresses table is never GC'ed. 76 sInstance.mCache.clear(); 77 while (c.moveToNext()) { 78 // TODO: don't hardcode the column indices 79 long id = c.getLong(0); 80 String number = c.getString(1); 81 sInstance.mCache.put(id, number); 82 } 83 } 84 } finally { 85 c.close(); 86 } 87 88 if (Log.isLoggable(LogTag.THREAD_CACHE, Log.VERBOSE)) { 89 LogTag.debug("[RecipientIdCache] fill: finished"); 90 dump(); 91 } 92 } 93 94 public static List<Entry> getAddresses(String spaceSepIds) { 95 synchronized (sInstance) { 96 List<Entry> numbers = new ArrayList<Entry>(); 97 String[] ids = spaceSepIds.split(" "); 98 for (String id : ids) { 99 long longId; 100 101 try { 102 longId = Long.parseLong(id); 103 } catch (NumberFormatException ex) { 104 // skip this id 105 continue; 106 } 107 108 String number = sInstance.mCache.get(longId); 109 110 if (number == null) { 111 Log.w(TAG, "RecipientId " + longId + " not in cache!"); 112 if (Log.isLoggable(LogTag.THREAD_CACHE, Log.VERBOSE)) { 113 dump(); 114 } 115 116 fill(); 117 number = sInstance.mCache.get(longId); 118 } 119 120 if (TextUtils.isEmpty(number)) { 121 Log.w(TAG, "RecipientId " + longId + " has empty number!"); 122 } else { 123 numbers.add(new Entry(longId, number)); 124 } 125 } 126 return numbers; 127 } 128 } 129 130 public static void updateNumbers(long threadId, ContactList contacts) { 131 long recipientId = 0; 132 133 for (Contact contact : contacts) { 134 if (contact.isNumberModified()) { 135 contact.setIsNumberModified(false); 136 } else { 137 // if the contact's number wasn't modified, don't bother. 138 continue; 139 } 140 141 recipientId = contact.getRecipientId(); 142 if (recipientId == 0) { 143 continue; 144 } 145 146 String number1 = contact.getNumber(); 147 String number2 = sInstance.mCache.get(recipientId); 148 149 if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 150 Log.d(TAG, "[RecipientIdCache] updateNumbers: comparing " + number1 + 151 " with " + number2); 152 } 153 154 // if the numbers don't match, let's update the RecipientIdCache's number 155 // with the new number in the contact. 156 if (!number1.equalsIgnoreCase(number2)) { 157 sInstance.mCache.put(recipientId, number1); 158 sInstance.updateCanonicalAddressInDb(recipientId, number1); 159 } 160 } 161 } 162 163 private void updateCanonicalAddressInDb(long id, String number) { 164 if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 165 Log.d(TAG, "[RecipientIdCache] updateCanonicalAddressInDb: id=" + id + 166 ", number=" + number); 167 } 168 169 ContentValues values = new ContentValues(); 170 values.put(Telephony.CanonicalAddressesColumns.ADDRESS, number); 171 172 StringBuilder buf = new StringBuilder(Telephony.CanonicalAddressesColumns._ID); 173 buf.append('=').append(id); 174 175 Uri uri = ContentUris.withAppendedId(sSingleCanonicalAddressUri, id); 176 mContext.getContentResolver().update(uri, values, buf.toString(), null); 177 } 178 179 public static void dump() { 180 // Only dump user private data if we're in special debug mode 181 synchronized (sInstance) { 182 Log.d(TAG, "*** Recipient ID cache dump ***"); 183 for (Long id : sInstance.mCache.keySet()) { 184 Log.d(TAG, id + ": " + sInstance.mCache.get(id)); 185 } 186 } 187 } 188} 189