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