• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.email.provider;
18 
19 import android.accounts.AccountManager;
20 import android.content.ContentResolver;
21 import android.content.ContentValues;
22 import android.content.Context;
23 import android.database.Cursor;
24 import android.database.SQLException;
25 import android.database.sqlite.SQLiteDatabase;
26 import android.database.sqlite.SQLiteOpenHelper;
27 import android.provider.ContactsContract;
28 import android.util.Log;
29 
30 import com.android.email.Email;
31 import com.android.emailcommon.AccountManagerTypes;
32 import com.android.emailcommon.CalendarProviderStub;
33 import com.android.emailcommon.mail.Address;
34 import com.android.emailcommon.provider.Account;
35 import com.android.emailcommon.provider.EmailContent;
36 import com.android.emailcommon.provider.EmailContent.AccountColumns;
37 import com.android.emailcommon.provider.EmailContent.Attachment;
38 import com.android.emailcommon.provider.EmailContent.AttachmentColumns;
39 import com.android.emailcommon.provider.EmailContent.Body;
40 import com.android.emailcommon.provider.EmailContent.BodyColumns;
41 import com.android.emailcommon.provider.EmailContent.HostAuthColumns;
42 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
43 import com.android.emailcommon.provider.EmailContent.Message;
44 import com.android.emailcommon.provider.EmailContent.MessageColumns;
45 import com.android.emailcommon.provider.EmailContent.PolicyColumns;
46 import com.android.emailcommon.provider.EmailContent.QuickResponseColumns;
47 import com.android.emailcommon.provider.EmailContent.SyncColumns;
48 import com.android.emailcommon.provider.HostAuth;
49 import com.android.emailcommon.provider.Mailbox;
50 import com.android.emailcommon.provider.Policy;
51 import com.android.emailcommon.provider.QuickResponse;
52 import com.android.emailcommon.service.LegacyPolicySet;
53 import com.google.common.annotations.VisibleForTesting;
54 
55 public final class DBHelper {
56     private static final String TAG = "EmailProvider";
57 
58     private static final String WHERE_ID = EmailContent.RECORD_ID + "=?";
59 
60     private static final String TRIGGER_MAILBOX_DELETE =
61         "create trigger mailbox_delete before delete on " + Mailbox.TABLE_NAME +
62         " begin" +
63         " delete from " + Message.TABLE_NAME +
64         "  where " + MessageColumns.MAILBOX_KEY + "=old." + EmailContent.RECORD_ID +
65         "; delete from " + Message.UPDATED_TABLE_NAME +
66         "  where " + MessageColumns.MAILBOX_KEY + "=old." + EmailContent.RECORD_ID +
67         "; delete from " + Message.DELETED_TABLE_NAME +
68         "  where " + MessageColumns.MAILBOX_KEY + "=old." + EmailContent.RECORD_ID +
69         "; end";
70 
71     private static final String TRIGGER_ACCOUNT_DELETE =
72         "create trigger account_delete before delete on " + Account.TABLE_NAME +
73         " begin delete from " + Mailbox.TABLE_NAME +
74         " where " + MailboxColumns.ACCOUNT_KEY + "=old." + EmailContent.RECORD_ID +
75         "; delete from " + HostAuth.TABLE_NAME +
76         " where " + EmailContent.RECORD_ID + "=old." + AccountColumns.HOST_AUTH_KEY_RECV +
77         "; delete from " + HostAuth.TABLE_NAME +
78         " where " + EmailContent.RECORD_ID + "=old." + AccountColumns.HOST_AUTH_KEY_SEND +
79         "; delete from " + Policy.TABLE_NAME +
80         " where " + EmailContent.RECORD_ID + "=old." + AccountColumns.POLICY_KEY +
81         "; end";
82 
83     // Any changes to the database format *must* include update-in-place code.
84     // Original version: 3
85     // Version 4: Database wipe required; changing AccountManager interface w/Exchange
86     // Version 5: Database wipe required; changing AccountManager interface w/Exchange
87     // Version 6: Adding Message.mServerTimeStamp column
88     // Version 7: Replace the mailbox_delete trigger with a version that removes orphaned messages
89     //            from the Message_Deletes and Message_Updates tables
90     // Version 8: Add security flags column to accounts table
91     // Version 9: Add security sync key and signature to accounts table
92     // Version 10: Add meeting info to message table
93     // Version 11: Add content and flags to attachment table
94     // Version 12: Add content_bytes to attachment table. content is deprecated.
95     // Version 13: Add messageCount to Mailbox table.
96     // Version 14: Add snippet to Message table
97     // Version 15: Fix upgrade problem in version 14.
98     // Version 16: Add accountKey to Attachment table
99     // Version 17: Add parentKey to Mailbox table
100     // Version 18: Copy Mailbox.displayName to Mailbox.serverId for all IMAP & POP3 mailboxes.
101     //             Column Mailbox.serverId is used for the server-side pathname of a mailbox.
102     // Version 19: Add Policy table; add policyKey to Account table and trigger to delete an
103     //             Account's policy when the Account is deleted
104     // Version 20: Add new policies to Policy table
105     // Version 21: Add lastSeenMessageKey column to Mailbox table
106     // Version 22: Upgrade path for IMAP/POP accounts to integrate with AccountManager
107     // Version 23: Add column to mailbox table for time of last access
108     // Version 24: Add column to hostauth table for client cert alias
109     // Version 25: Added QuickResponse table
110     // Version 26: Update IMAP accounts to add FLAG_SUPPORTS_SEARCH flag
111     // Version 27: Add protocolSearchInfo to Message table
112     // Version 28: Add notifiedMessageId and notifiedMessageCount to Account
113     // Version 29: Add protocolPoliciesEnforced and protocolPoliciesUnsupported to Policy
114     // Version 30: Use CSV of RFC822 addresses instead of "packed" values
115     // Version 31: Add columns to mailbox for ui status/last result
116     // Version 32: Add columns to mailbox for last notified message key/count; insure not null
117     //             for "notified" columns
118     // Version 33: Add columns to attachment for ui provider columns
119     // Version 34: Add total count to mailbox
120     // Version 35: Set up defaults for lastTouchedCount for drafts and sent
121     // Version 36: mblank intentionally left this space
122     // Version 37: Add flag for settings support in folders
123     // Version 38&39: Add threadTopic to message (for future support)
124 
125     // Versions 100+ are in Email2
126 
127     public static final int DATABASE_VERSION = 39;
128 
129     // Any changes to the database format *must* include update-in-place code.
130     // Original version: 2
131     // Version 3: Add "sourceKey" column
132     // Version 4: Database wipe required; changing AccountManager interface w/Exchange
133     // Version 5: Database wipe required; changing AccountManager interface w/Exchange
134     // Version 6: Adding Body.mIntroText column
135     // Version 7/8: Adding quoted text start pos
136 
137     // Versions 100+ are in Email2
138 
139     public static final int BODY_DATABASE_VERSION = 8;
140 
141     /*
142      * Internal helper method for index creation.
143      * Example:
144      * "create index message_" + MessageColumns.FLAG_READ
145      * + " on " + Message.TABLE_NAME + " (" + MessageColumns.FLAG_READ + ");"
146      */
147     /* package */
createIndex(String tableName, String columnName)148     static String createIndex(String tableName, String columnName) {
149         return "create index " + tableName.toLowerCase() + '_' + columnName
150             + " on " + tableName + " (" + columnName + ");";
151     }
152 
createMessageTable(SQLiteDatabase db)153     static void createMessageTable(SQLiteDatabase db) {
154         String messageColumns = MessageColumns.DISPLAY_NAME + " text, "
155             + MessageColumns.TIMESTAMP + " integer, "
156             + MessageColumns.SUBJECT + " text, "
157             + MessageColumns.FLAG_READ + " integer, "
158             + MessageColumns.FLAG_LOADED + " integer, "
159             + MessageColumns.FLAG_FAVORITE + " integer, "
160             + MessageColumns.FLAG_ATTACHMENT + " integer, "
161             + MessageColumns.FLAGS + " integer, "
162             + MessageColumns.CLIENT_ID + " integer, "
163             + MessageColumns.MESSAGE_ID + " text, "
164             + MessageColumns.MAILBOX_KEY + " integer, "
165             + MessageColumns.ACCOUNT_KEY + " integer, "
166             + MessageColumns.FROM_LIST + " text, "
167             + MessageColumns.TO_LIST + " text, "
168             + MessageColumns.CC_LIST + " text, "
169             + MessageColumns.BCC_LIST + " text, "
170             + MessageColumns.REPLY_TO_LIST + " text, "
171             + MessageColumns.MEETING_INFO + " text, "
172             + MessageColumns.SNIPPET + " text, "
173             + MessageColumns.PROTOCOL_SEARCH_INFO + " text, "
174             + MessageColumns.THREAD_TOPIC + " text"
175             + ");";
176 
177         // This String and the following String MUST have the same columns, except for the type
178         // of those columns!
179         String createString = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
180             + SyncColumns.SERVER_ID + " text, "
181             + SyncColumns.SERVER_TIMESTAMP + " integer, "
182             + messageColumns;
183 
184         // For the updated and deleted tables, the id is assigned, but we do want to keep track
185         // of the ORDER of updates using an autoincrement primary key.  We use the DATA column
186         // at this point; it has no other function
187         String altCreateString = " (" + EmailContent.RECORD_ID + " integer unique, "
188             + SyncColumns.SERVER_ID + " text, "
189             + SyncColumns.SERVER_TIMESTAMP + " integer, "
190             + messageColumns;
191 
192         // The three tables have the same schema
193         db.execSQL("create table " + Message.TABLE_NAME + createString);
194         db.execSQL("create table " + Message.UPDATED_TABLE_NAME + altCreateString);
195         db.execSQL("create table " + Message.DELETED_TABLE_NAME + altCreateString);
196 
197         String indexColumns[] = {
198             MessageColumns.TIMESTAMP,
199             MessageColumns.FLAG_READ,
200             MessageColumns.FLAG_LOADED,
201             MessageColumns.MAILBOX_KEY,
202             SyncColumns.SERVER_ID
203         };
204 
205         for (String columnName : indexColumns) {
206             db.execSQL(createIndex(Message.TABLE_NAME, columnName));
207         }
208 
209         // Deleting a Message deletes all associated Attachments
210         // Deleting the associated Body cannot be done in a trigger, because the Body is stored
211         // in a separate database, and trigger cannot operate on attached databases.
212         db.execSQL("create trigger message_delete before delete on " + Message.TABLE_NAME +
213                 " begin delete from " + Attachment.TABLE_NAME +
214                 "  where " + AttachmentColumns.MESSAGE_KEY + "=old." + EmailContent.RECORD_ID +
215                 "; end");
216 
217         // Add triggers to keep unread count accurate per mailbox
218 
219         // NOTE: SQLite's before triggers are not safe when recursive triggers are involved.
220         // Use caution when changing them.
221 
222         // Insert a message; if flagRead is zero, add to the unread count of the message's mailbox
223         db.execSQL("create trigger unread_message_insert before insert on " + Message.TABLE_NAME +
224                 " when NEW." + MessageColumns.FLAG_READ + "=0" +
225                 " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UNREAD_COUNT +
226                 '=' + MailboxColumns.UNREAD_COUNT + "+1" +
227                 "  where " + EmailContent.RECORD_ID + "=NEW." + MessageColumns.MAILBOX_KEY +
228                 "; end");
229 
230         // Delete a message; if flagRead is zero, decrement the unread count of the msg's mailbox
231         db.execSQL("create trigger unread_message_delete before delete on " + Message.TABLE_NAME +
232                 " when OLD." + MessageColumns.FLAG_READ + "=0" +
233                 " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UNREAD_COUNT +
234                 '=' + MailboxColumns.UNREAD_COUNT + "-1" +
235                 "  where " + EmailContent.RECORD_ID + "=OLD." + MessageColumns.MAILBOX_KEY +
236                 "; end");
237 
238         // Change a message's mailbox
239         db.execSQL("create trigger unread_message_move before update of " +
240                 MessageColumns.MAILBOX_KEY + " on " + Message.TABLE_NAME +
241                 " when OLD." + MessageColumns.FLAG_READ + "=0" +
242                 " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UNREAD_COUNT +
243                 '=' + MailboxColumns.UNREAD_COUNT + "-1" +
244                 "  where " + EmailContent.RECORD_ID + "=OLD." + MessageColumns.MAILBOX_KEY +
245                 "; update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UNREAD_COUNT +
246                 '=' + MailboxColumns.UNREAD_COUNT + "+1" +
247                 " where " + EmailContent.RECORD_ID + "=NEW." + MessageColumns.MAILBOX_KEY +
248                 "; end");
249 
250         // Change a message's read state
251         db.execSQL("create trigger unread_message_read before update of " +
252                 MessageColumns.FLAG_READ + " on " + Message.TABLE_NAME +
253                 " when OLD." + MessageColumns.FLAG_READ + "!=NEW." + MessageColumns.FLAG_READ +
254                 " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.UNREAD_COUNT +
255                 '=' + MailboxColumns.UNREAD_COUNT + "+ case OLD." + MessageColumns.FLAG_READ +
256                 " when 0 then -1 else 1 end" +
257                 "  where " + EmailContent.RECORD_ID + "=OLD." + MessageColumns.MAILBOX_KEY +
258                 "; end");
259 
260         // Add triggers to update message count per mailbox
261 
262         // Insert a message.
263         db.execSQL("create trigger message_count_message_insert after insert on " +
264                 Message.TABLE_NAME +
265                 " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.MESSAGE_COUNT +
266                 '=' + MailboxColumns.MESSAGE_COUNT + "+1" +
267                 "  where " + EmailContent.RECORD_ID + "=NEW." + MessageColumns.MAILBOX_KEY +
268                 "; end");
269 
270         // Delete a message; if flagRead is zero, decrement the unread count of the msg's mailbox
271         db.execSQL("create trigger message_count_message_delete after delete on " +
272                 Message.TABLE_NAME +
273                 " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.MESSAGE_COUNT +
274                 '=' + MailboxColumns.MESSAGE_COUNT + "-1" +
275                 "  where " + EmailContent.RECORD_ID + "=OLD." + MessageColumns.MAILBOX_KEY +
276                 "; end");
277 
278         // Change a message's mailbox
279         db.execSQL("create trigger message_count_message_move after update of " +
280                 MessageColumns.MAILBOX_KEY + " on " + Message.TABLE_NAME +
281                 " begin update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.MESSAGE_COUNT +
282                 '=' + MailboxColumns.MESSAGE_COUNT + "-1" +
283                 "  where " + EmailContent.RECORD_ID + "=OLD." + MessageColumns.MAILBOX_KEY +
284                 "; update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.MESSAGE_COUNT +
285                 '=' + MailboxColumns.MESSAGE_COUNT + "+1" +
286                 " where " + EmailContent.RECORD_ID + "=NEW." + MessageColumns.MAILBOX_KEY +
287                 "; end");
288     }
289 
resetMessageTable(SQLiteDatabase db, int oldVersion, int newVersion)290     static void resetMessageTable(SQLiteDatabase db, int oldVersion, int newVersion) {
291         try {
292             db.execSQL("drop table " + Message.TABLE_NAME);
293             db.execSQL("drop table " + Message.UPDATED_TABLE_NAME);
294             db.execSQL("drop table " + Message.DELETED_TABLE_NAME);
295         } catch (SQLException e) {
296         }
297         createMessageTable(db);
298     }
299 
300     @SuppressWarnings("deprecation")
createAccountTable(SQLiteDatabase db)301     static void createAccountTable(SQLiteDatabase db) {
302         String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
303             + AccountColumns.DISPLAY_NAME + " text, "
304             + AccountColumns.EMAIL_ADDRESS + " text, "
305             + AccountColumns.SYNC_KEY + " text, "
306             + AccountColumns.SYNC_LOOKBACK + " integer, "
307             + AccountColumns.SYNC_INTERVAL + " text, "
308             + AccountColumns.HOST_AUTH_KEY_RECV + " integer, "
309             + AccountColumns.HOST_AUTH_KEY_SEND + " integer, "
310             + AccountColumns.FLAGS + " integer, "
311             + AccountColumns.IS_DEFAULT + " integer, "
312             + AccountColumns.COMPATIBILITY_UUID + " text, "
313             + AccountColumns.SENDER_NAME + " text, "
314             + AccountColumns.RINGTONE_URI + " text, "
315             + AccountColumns.PROTOCOL_VERSION + " text, "
316             + AccountColumns.NEW_MESSAGE_COUNT + " integer, "
317             + AccountColumns.SECURITY_FLAGS + " integer, "
318             + AccountColumns.SECURITY_SYNC_KEY + " text, "
319             + AccountColumns.SIGNATURE + " text, "
320             + AccountColumns.POLICY_KEY + " integer, "
321             + AccountColumns.NOTIFIED_MESSAGE_ID + " integer, "
322             + AccountColumns.NOTIFIED_MESSAGE_COUNT + " integer"
323             + ");";
324         db.execSQL("create table " + Account.TABLE_NAME + s);
325         // Deleting an account deletes associated Mailboxes and HostAuth's
326         db.execSQL(TRIGGER_ACCOUNT_DELETE);
327     }
328 
resetAccountTable(SQLiteDatabase db, int oldVersion, int newVersion)329     static void resetAccountTable(SQLiteDatabase db, int oldVersion, int newVersion) {
330         try {
331             db.execSQL("drop table " +  Account.TABLE_NAME);
332         } catch (SQLException e) {
333         }
334         createAccountTable(db);
335     }
336 
createPolicyTable(SQLiteDatabase db)337     static void createPolicyTable(SQLiteDatabase db) {
338         String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
339             + PolicyColumns.PASSWORD_MODE + " integer, "
340             + PolicyColumns.PASSWORD_MIN_LENGTH + " integer, "
341             + PolicyColumns.PASSWORD_EXPIRATION_DAYS + " integer, "
342             + PolicyColumns.PASSWORD_HISTORY + " integer, "
343             + PolicyColumns.PASSWORD_COMPLEX_CHARS + " integer, "
344             + PolicyColumns.PASSWORD_MAX_FAILS + " integer, "
345             + PolicyColumns.MAX_SCREEN_LOCK_TIME + " integer, "
346             + PolicyColumns.REQUIRE_REMOTE_WIPE + " integer, "
347             + PolicyColumns.REQUIRE_ENCRYPTION + " integer, "
348             + PolicyColumns.REQUIRE_ENCRYPTION_EXTERNAL + " integer, "
349             + PolicyColumns.REQUIRE_MANUAL_SYNC_WHEN_ROAMING + " integer, "
350             + PolicyColumns.DONT_ALLOW_CAMERA + " integer, "
351             + PolicyColumns.DONT_ALLOW_ATTACHMENTS + " integer, "
352             + PolicyColumns.DONT_ALLOW_HTML + " integer, "
353             + PolicyColumns.MAX_ATTACHMENT_SIZE + " integer, "
354             + PolicyColumns.MAX_TEXT_TRUNCATION_SIZE + " integer, "
355             + PolicyColumns.MAX_HTML_TRUNCATION_SIZE + " integer, "
356             + PolicyColumns.MAX_EMAIL_LOOKBACK + " integer, "
357             + PolicyColumns.MAX_CALENDAR_LOOKBACK + " integer, "
358             + PolicyColumns.PASSWORD_RECOVERY_ENABLED + " integer, "
359             + PolicyColumns.PROTOCOL_POLICIES_ENFORCED + " text, "
360             + PolicyColumns.PROTOCOL_POLICIES_UNSUPPORTED + " text"
361             + ");";
362         db.execSQL("create table " + Policy.TABLE_NAME + s);
363     }
364 
createHostAuthTable(SQLiteDatabase db)365     static void createHostAuthTable(SQLiteDatabase db) {
366         String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
367             + HostAuthColumns.PROTOCOL + " text, "
368             + HostAuthColumns.ADDRESS + " text, "
369             + HostAuthColumns.PORT + " integer, "
370             + HostAuthColumns.FLAGS + " integer, "
371             + HostAuthColumns.LOGIN + " text, "
372             + HostAuthColumns.PASSWORD + " text, "
373             + HostAuthColumns.DOMAIN + " text, "
374             + HostAuthColumns.ACCOUNT_KEY + " integer,"
375             + HostAuthColumns.CLIENT_CERT_ALIAS + " text"
376             + ");";
377         db.execSQL("create table " + HostAuth.TABLE_NAME + s);
378     }
379 
resetHostAuthTable(SQLiteDatabase db, int oldVersion, int newVersion)380     static void resetHostAuthTable(SQLiteDatabase db, int oldVersion, int newVersion) {
381         try {
382             db.execSQL("drop table " + HostAuth.TABLE_NAME);
383         } catch (SQLException e) {
384         }
385         createHostAuthTable(db);
386     }
387 
createMailboxTable(SQLiteDatabase db)388     static void createMailboxTable(SQLiteDatabase db) {
389         String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
390             + MailboxColumns.DISPLAY_NAME + " text, "
391             + MailboxColumns.SERVER_ID + " text, "
392             + MailboxColumns.PARENT_SERVER_ID + " text, "
393             + MailboxColumns.PARENT_KEY + " integer, "
394             + MailboxColumns.ACCOUNT_KEY + " integer, "
395             + MailboxColumns.TYPE + " integer, "
396             + MailboxColumns.DELIMITER + " integer, "
397             + MailboxColumns.SYNC_KEY + " text, "
398             + MailboxColumns.SYNC_LOOKBACK + " integer, "
399             + MailboxColumns.SYNC_INTERVAL + " integer, "
400             + MailboxColumns.SYNC_TIME + " integer, "
401             + MailboxColumns.UNREAD_COUNT + " integer, "
402             + MailboxColumns.FLAG_VISIBLE + " integer, "
403             + MailboxColumns.FLAGS + " integer, "
404             + MailboxColumns.VISIBLE_LIMIT + " integer, "
405             + MailboxColumns.SYNC_STATUS + " text, "
406             + MailboxColumns.MESSAGE_COUNT + " integer not null default 0, "
407             + MailboxColumns.LAST_TOUCHED_TIME + " integer default 0, "
408             + MailboxColumns.UI_SYNC_STATUS + " integer default 0, "
409             + MailboxColumns.UI_LAST_SYNC_RESULT + " integer default 0, "
410             + MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY + " integer not null default 0, "
411             + MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT + " integer not null default 0, "
412             + MailboxColumns.TOTAL_COUNT + " integer, "
413             + MailboxColumns.LAST_SEEN_MESSAGE_KEY + " integer"
414             + ");";
415         db.execSQL("create table " + Mailbox.TABLE_NAME + s);
416         db.execSQL("create index mailbox_" + MailboxColumns.SERVER_ID
417                 + " on " + Mailbox.TABLE_NAME + " (" + MailboxColumns.SERVER_ID + ")");
418         db.execSQL("create index mailbox_" + MailboxColumns.ACCOUNT_KEY
419                 + " on " + Mailbox.TABLE_NAME + " (" + MailboxColumns.ACCOUNT_KEY + ")");
420         // Deleting a Mailbox deletes associated Messages in all three tables
421         db.execSQL(TRIGGER_MAILBOX_DELETE);
422     }
423 
resetMailboxTable(SQLiteDatabase db, int oldVersion, int newVersion)424     static void resetMailboxTable(SQLiteDatabase db, int oldVersion, int newVersion) {
425         try {
426             db.execSQL("drop table " + Mailbox.TABLE_NAME);
427         } catch (SQLException e) {
428         }
429         createMailboxTable(db);
430     }
431 
createAttachmentTable(SQLiteDatabase db)432     static void createAttachmentTable(SQLiteDatabase db) {
433         String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
434             + AttachmentColumns.FILENAME + " text, "
435             + AttachmentColumns.MIME_TYPE + " text, "
436             + AttachmentColumns.SIZE + " integer, "
437             + AttachmentColumns.CONTENT_ID + " text, "
438             + AttachmentColumns.CONTENT_URI + " text, "
439             + AttachmentColumns.MESSAGE_KEY + " integer, "
440             + AttachmentColumns.LOCATION + " text, "
441             + AttachmentColumns.ENCODING + " text, "
442             + AttachmentColumns.CONTENT + " text, "
443             + AttachmentColumns.FLAGS + " integer, "
444             + AttachmentColumns.CONTENT_BYTES + " blob, "
445             + AttachmentColumns.ACCOUNT_KEY + " integer, "
446             + AttachmentColumns.UI_STATE + " integer, "
447             + AttachmentColumns.UI_DESTINATION + " integer, "
448             + AttachmentColumns.UI_DOWNLOADED_SIZE + " integer"
449             + ");";
450         db.execSQL("create table " + Attachment.TABLE_NAME + s);
451         db.execSQL(createIndex(Attachment.TABLE_NAME, AttachmentColumns.MESSAGE_KEY));
452     }
453 
resetAttachmentTable(SQLiteDatabase db, int oldVersion, int newVersion)454     static void resetAttachmentTable(SQLiteDatabase db, int oldVersion, int newVersion) {
455         try {
456             db.execSQL("drop table " + Attachment.TABLE_NAME);
457         } catch (SQLException e) {
458         }
459         createAttachmentTable(db);
460     }
461 
createQuickResponseTable(SQLiteDatabase db)462     static void createQuickResponseTable(SQLiteDatabase db) {
463         String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
464                 + QuickResponseColumns.TEXT + " text, "
465                 + QuickResponseColumns.ACCOUNT_KEY + " integer"
466                 + ");";
467         db.execSQL("create table " + QuickResponse.TABLE_NAME + s);
468     }
469 
createBodyTable(SQLiteDatabase db)470     static void createBodyTable(SQLiteDatabase db) {
471         String s = " (" + EmailContent.RECORD_ID + " integer primary key autoincrement, "
472             + BodyColumns.MESSAGE_KEY + " integer, "
473             + BodyColumns.HTML_CONTENT + " text, "
474             + BodyColumns.TEXT_CONTENT + " text, "
475             + BodyColumns.HTML_REPLY + " text, "
476             + BodyColumns.TEXT_REPLY + " text, "
477             + BodyColumns.SOURCE_MESSAGE_KEY + " text, "
478             + BodyColumns.INTRO_TEXT + " text, "
479             + BodyColumns.QUOTED_TEXT_START_POS + " integer"
480             + ");";
481         db.execSQL("create table " + Body.TABLE_NAME + s);
482         db.execSQL(createIndex(Body.TABLE_NAME, BodyColumns.MESSAGE_KEY));
483     }
484 
upgradeBodyTable(SQLiteDatabase db, int oldVersion, int newVersion)485     static void upgradeBodyTable(SQLiteDatabase db, int oldVersion, int newVersion) {
486         if (oldVersion < 5) {
487             try {
488                 db.execSQL("drop table " + Body.TABLE_NAME);
489                 createBodyTable(db);
490                 oldVersion = 5;
491             } catch (SQLException e) {
492             }
493         }
494         if (oldVersion == 5) {
495             try {
496                 db.execSQL("alter table " + Body.TABLE_NAME
497                         + " add " + BodyColumns.INTRO_TEXT + " text");
498             } catch (SQLException e) {
499                 // Shouldn't be needed unless we're debugging and interrupt the process
500                 Log.w(TAG, "Exception upgrading EmailProviderBody.db from v5 to v6", e);
501             }
502             oldVersion = 6;
503         }
504         if (oldVersion == 6 || oldVersion ==7) {
505             try {
506                 db.execSQL("alter table " + Body.TABLE_NAME
507                         + " add " + BodyColumns.QUOTED_TEXT_START_POS + " integer");
508             } catch (SQLException e) {
509                 // Shouldn't be needed unless we're debugging and interrupt the process
510                 Log.w(TAG, "Exception upgrading EmailProviderBody.db from v6 to v8", e);
511             }
512             oldVersion = 8;
513         }
514     }
515 
516     protected static class BodyDatabaseHelper extends SQLiteOpenHelper {
BodyDatabaseHelper(Context context, String name)517         BodyDatabaseHelper(Context context, String name) {
518             super(context, name, null, BODY_DATABASE_VERSION);
519         }
520 
521         @Override
onCreate(SQLiteDatabase db)522         public void onCreate(SQLiteDatabase db) {
523             Log.d(TAG, "Creating EmailProviderBody database");
524             createBodyTable(db);
525         }
526 
527         @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)528         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
529             upgradeBodyTable(db, oldVersion, newVersion);
530         }
531 
532         @Override
onOpen(SQLiteDatabase db)533         public void onOpen(SQLiteDatabase db) {
534         }
535     }
536 
537     /** Counts the number of messages in each mailbox, and updates the message count column. */
538     @VisibleForTesting
recalculateMessageCount(SQLiteDatabase db)539     static void recalculateMessageCount(SQLiteDatabase db) {
540         db.execSQL("update " + Mailbox.TABLE_NAME + " set " + MailboxColumns.MESSAGE_COUNT +
541                 "= (select count(*) from " + Message.TABLE_NAME +
542                 " where " + Message.MAILBOX_KEY + " = " +
543                     Mailbox.TABLE_NAME + "." + EmailContent.RECORD_ID + ")");
544     }
545 
546     protected static class DatabaseHelper extends SQLiteOpenHelper {
547         Context mContext;
548 
DatabaseHelper(Context context, String name)549         DatabaseHelper(Context context, String name) {
550             super(context, name, null, DATABASE_VERSION);
551             mContext = context;
552         }
553 
554         @Override
onCreate(SQLiteDatabase db)555         public void onCreate(SQLiteDatabase db) {
556             Log.d(TAG, "Creating EmailProvider database");
557             // Create all tables here; each class has its own method
558             createMessageTable(db);
559             createAttachmentTable(db);
560             createMailboxTable(db);
561             createHostAuthTable(db);
562             createAccountTable(db);
563             createPolicyTable(db);
564             createQuickResponseTable(db);
565         }
566 
567         @Override
568         @SuppressWarnings("deprecation")
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)569         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
570             // For versions prior to 5, delete all data
571             // Versions >= 5 require that data be preserved!
572             if (oldVersion < 5) {
573                 android.accounts.Account[] accounts = AccountManager.get(mContext)
574                         .getAccountsByType(AccountManagerTypes.TYPE_EXCHANGE);
575                 for (android.accounts.Account account: accounts) {
576                     AccountManager.get(mContext).removeAccount(account, null, null);
577                 }
578                 resetMessageTable(db, oldVersion, newVersion);
579                 resetAttachmentTable(db, oldVersion, newVersion);
580                 resetMailboxTable(db, oldVersion, newVersion);
581                 resetHostAuthTable(db, oldVersion, newVersion);
582                 resetAccountTable(db, oldVersion, newVersion);
583                 return;
584             }
585             if (oldVersion == 5) {
586                 // Message Tables: Add SyncColumns.SERVER_TIMESTAMP
587                 try {
588                     db.execSQL("alter table " + Message.TABLE_NAME
589                             + " add column " + SyncColumns.SERVER_TIMESTAMP + " integer" + ";");
590                     db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
591                             + " add column " + SyncColumns.SERVER_TIMESTAMP + " integer" + ";");
592                     db.execSQL("alter table " + Message.DELETED_TABLE_NAME
593                             + " add column " + SyncColumns.SERVER_TIMESTAMP + " integer" + ";");
594                 } catch (SQLException e) {
595                     // Shouldn't be needed unless we're debugging and interrupt the process
596                     Log.w(TAG, "Exception upgrading EmailProvider.db from v5 to v6", e);
597                 }
598                 oldVersion = 6;
599             }
600             if (oldVersion == 6) {
601                 // Use the newer mailbox_delete trigger
602                 db.execSQL("drop trigger mailbox_delete;");
603                 db.execSQL(TRIGGER_MAILBOX_DELETE);
604                 oldVersion = 7;
605             }
606             if (oldVersion == 7) {
607                 // add the security (provisioning) column
608                 try {
609                     db.execSQL("alter table " + Account.TABLE_NAME
610                             + " add column " + AccountColumns.SECURITY_FLAGS + " integer" + ";");
611                 } catch (SQLException e) {
612                     // Shouldn't be needed unless we're debugging and interrupt the process
613                     Log.w(TAG, "Exception upgrading EmailProvider.db from 7 to 8 " + e);
614                 }
615                 oldVersion = 8;
616             }
617             if (oldVersion == 8) {
618                 // accounts: add security sync key & user signature columns
619                 try {
620                     db.execSQL("alter table " + Account.TABLE_NAME
621                             + " add column " + AccountColumns.SECURITY_SYNC_KEY + " text" + ";");
622                     db.execSQL("alter table " + Account.TABLE_NAME
623                             + " add column " + AccountColumns.SIGNATURE + " text" + ";");
624                 } catch (SQLException e) {
625                     // Shouldn't be needed unless we're debugging and interrupt the process
626                     Log.w(TAG, "Exception upgrading EmailProvider.db from 8 to 9 " + e);
627                 }
628                 oldVersion = 9;
629             }
630             if (oldVersion == 9) {
631                 // Message: add meeting info column into Message tables
632                 try {
633                     db.execSQL("alter table " + Message.TABLE_NAME
634                             + " add column " + MessageColumns.MEETING_INFO + " text" + ";");
635                     db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
636                             + " add column " + MessageColumns.MEETING_INFO + " text" + ";");
637                     db.execSQL("alter table " + Message.DELETED_TABLE_NAME
638                             + " add column " + MessageColumns.MEETING_INFO + " text" + ";");
639                 } catch (SQLException e) {
640                     // Shouldn't be needed unless we're debugging and interrupt the process
641                     Log.w(TAG, "Exception upgrading EmailProvider.db from 9 to 10 " + e);
642                 }
643                 oldVersion = 10;
644             }
645             if (oldVersion == 10) {
646                 // Attachment: add content and flags columns
647                 try {
648                     db.execSQL("alter table " + Attachment.TABLE_NAME
649                             + " add column " + AttachmentColumns.CONTENT + " text" + ";");
650                     db.execSQL("alter table " + Attachment.TABLE_NAME
651                             + " add column " + AttachmentColumns.FLAGS + " integer" + ";");
652                 } catch (SQLException e) {
653                     // Shouldn't be needed unless we're debugging and interrupt the process
654                     Log.w(TAG, "Exception upgrading EmailProvider.db from 10 to 11 " + e);
655                 }
656                 oldVersion = 11;
657             }
658             if (oldVersion == 11) {
659                 // Attachment: add content_bytes
660                 try {
661                     db.execSQL("alter table " + Attachment.TABLE_NAME
662                             + " add column " + AttachmentColumns.CONTENT_BYTES + " blob" + ";");
663                 } catch (SQLException e) {
664                     // Shouldn't be needed unless we're debugging and interrupt the process
665                     Log.w(TAG, "Exception upgrading EmailProvider.db from 11 to 12 " + e);
666                 }
667                 oldVersion = 12;
668             }
669             if (oldVersion == 12) {
670                 try {
671                     db.execSQL("alter table " + Mailbox.TABLE_NAME
672                             + " add column " + Mailbox.MESSAGE_COUNT
673                                     +" integer not null default 0" + ";");
674                     recalculateMessageCount(db);
675                 } catch (SQLException e) {
676                     // Shouldn't be needed unless we're debugging and interrupt the process
677                     Log.w(TAG, "Exception upgrading EmailProvider.db from 12 to 13 " + e);
678                 }
679                 oldVersion = 13;
680             }
681             if (oldVersion == 13) {
682                 try {
683                     db.execSQL("alter table " + Message.TABLE_NAME
684                             + " add column " + Message.SNIPPET
685                                     +" text" + ";");
686                 } catch (SQLException e) {
687                     // Shouldn't be needed unless we're debugging and interrupt the process
688                     Log.w(TAG, "Exception upgrading EmailProvider.db from 13 to 14 " + e);
689                 }
690                 oldVersion = 14;
691             }
692             if (oldVersion == 14) {
693                 try {
694                     db.execSQL("alter table " + Message.DELETED_TABLE_NAME
695                             + " add column " + Message.SNIPPET +" text" + ";");
696                     db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
697                             + " add column " + Message.SNIPPET +" text" + ";");
698                 } catch (SQLException e) {
699                     // Shouldn't be needed unless we're debugging and interrupt the process
700                     Log.w(TAG, "Exception upgrading EmailProvider.db from 14 to 15 " + e);
701                 }
702                 oldVersion = 15;
703             }
704             if (oldVersion == 15) {
705                 try {
706                     db.execSQL("alter table " + Attachment.TABLE_NAME
707                             + " add column " + Attachment.ACCOUNT_KEY +" integer" + ";");
708                     // Update all existing attachments to add the accountKey data
709                     db.execSQL("update " + Attachment.TABLE_NAME + " set " +
710                             Attachment.ACCOUNT_KEY + "= (SELECT " + Message.TABLE_NAME + "." +
711                             Message.ACCOUNT_KEY + " from " + Message.TABLE_NAME + " where " +
712                             Message.TABLE_NAME + "." + Message.RECORD_ID + " = " +
713                             Attachment.TABLE_NAME + "." + Attachment.MESSAGE_KEY + ")");
714                 } catch (SQLException e) {
715                     // Shouldn't be needed unless we're debugging and interrupt the process
716                     Log.w(TAG, "Exception upgrading EmailProvider.db from 15 to 16 " + e);
717                 }
718                 oldVersion = 16;
719             }
720             if (oldVersion == 16) {
721                 try {
722                     db.execSQL("alter table " + Mailbox.TABLE_NAME
723                             + " add column " + Mailbox.PARENT_KEY + " integer;");
724                 } catch (SQLException e) {
725                     // Shouldn't be needed unless we're debugging and interrupt the process
726                     Log.w(TAG, "Exception upgrading EmailProvider.db from 16 to 17 " + e);
727                 }
728                 oldVersion = 17;
729             }
730             if (oldVersion == 17) {
731                 upgradeFromVersion17ToVersion18(db);
732                 oldVersion = 18;
733             }
734             if (oldVersion == 18) {
735                 try {
736                     db.execSQL("alter table " + Account.TABLE_NAME
737                             + " add column " + Account.POLICY_KEY + " integer;");
738                     db.execSQL("drop trigger account_delete;");
739                     db.execSQL(TRIGGER_ACCOUNT_DELETE);
740                     createPolicyTable(db);
741                     convertPolicyFlagsToPolicyTable(db);
742                 } catch (SQLException e) {
743                     // Shouldn't be needed unless we're debugging and interrupt the process
744                     Log.w(TAG, "Exception upgrading EmailProvider.db from 18 to 19 " + e);
745                 }
746                 oldVersion = 19;
747             }
748             if (oldVersion == 19) {
749                 try {
750                     db.execSQL("alter table " + Policy.TABLE_NAME
751                             + " add column " + PolicyColumns.REQUIRE_MANUAL_SYNC_WHEN_ROAMING +
752                             " integer;");
753                     db.execSQL("alter table " + Policy.TABLE_NAME
754                             + " add column " + PolicyColumns.DONT_ALLOW_CAMERA + " integer;");
755                     db.execSQL("alter table " + Policy.TABLE_NAME
756                             + " add column " + PolicyColumns.DONT_ALLOW_ATTACHMENTS + " integer;");
757                     db.execSQL("alter table " + Policy.TABLE_NAME
758                             + " add column " + PolicyColumns.DONT_ALLOW_HTML + " integer;");
759                     db.execSQL("alter table " + Policy.TABLE_NAME
760                             + " add column " + PolicyColumns.MAX_ATTACHMENT_SIZE + " integer;");
761                     db.execSQL("alter table " + Policy.TABLE_NAME
762                             + " add column " + PolicyColumns.MAX_TEXT_TRUNCATION_SIZE +
763                             " integer;");
764                     db.execSQL("alter table " + Policy.TABLE_NAME
765                             + " add column " + PolicyColumns.MAX_HTML_TRUNCATION_SIZE +
766                             " integer;");
767                     db.execSQL("alter table " + Policy.TABLE_NAME
768                             + " add column " + PolicyColumns.MAX_EMAIL_LOOKBACK + " integer;");
769                     db.execSQL("alter table " + Policy.TABLE_NAME
770                             + " add column " + PolicyColumns.MAX_CALENDAR_LOOKBACK + " integer;");
771                     db.execSQL("alter table " + Policy.TABLE_NAME
772                             + " add column " + PolicyColumns.PASSWORD_RECOVERY_ENABLED +
773                             " integer;");
774                 } catch (SQLException e) {
775                     // Shouldn't be needed unless we're debugging and interrupt the process
776                     Log.w(TAG, "Exception upgrading EmailProvider.db from 19 to 20 " + e);
777                 }
778                 oldVersion = 20;
779             }
780             if (oldVersion == 20) {
781                 try {
782                     db.execSQL("alter table " + Mailbox.TABLE_NAME
783                             + " add column " + Mailbox.LAST_SEEN_MESSAGE_KEY + " integer;");
784                 } catch (SQLException e) {
785                     // Shouldn't be needed unless we're debugging and interrupt the process
786                     Log.w(TAG, "Exception upgrading EmailProvider.db from 20 to 21 " + e);
787                 }
788                 oldVersion = 21;
789             }
790             if (oldVersion == 21) {
791                 upgradeFromVersion21ToVersion22(db, mContext);
792                 oldVersion = 22;
793             }
794             if (oldVersion == 22) {
795                 upgradeFromVersion22ToVersion23(db);
796                 oldVersion = 23;
797             }
798             if (oldVersion == 23) {
799                 upgradeFromVersion23ToVersion24(db);
800                 oldVersion = 24;
801             }
802             if (oldVersion == 24) {
803                 upgradeFromVersion24ToVersion25(db);
804                 oldVersion = 25;
805             }
806             if (oldVersion == 25) {
807                 upgradeFromVersion25ToVersion26(db);
808                 oldVersion = 26;
809             }
810             if (oldVersion == 26) {
811                 try {
812                     db.execSQL("alter table " + Message.TABLE_NAME
813                             + " add column " + Message.PROTOCOL_SEARCH_INFO + " text;");
814                     db.execSQL("alter table " + Message.DELETED_TABLE_NAME
815                             + " add column " + Message.PROTOCOL_SEARCH_INFO +" text" + ";");
816                     db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
817                             + " add column " + Message.PROTOCOL_SEARCH_INFO +" text" + ";");
818                 } catch (SQLException e) {
819                     // Shouldn't be needed unless we're debugging and interrupt the process
820                     Log.w(TAG, "Exception upgrading EmailProvider.db from 26 to 27 " + e);
821                 }
822                 oldVersion = 27;
823             }
824             if (oldVersion == 27) {
825                 try {
826                     db.execSQL("alter table " + Account.TABLE_NAME
827                             + " add column " + Account.NOTIFIED_MESSAGE_ID + " integer;");
828                     db.execSQL("alter table " + Account.TABLE_NAME
829                             + " add column " + Account.NOTIFIED_MESSAGE_COUNT + " integer;");
830                 } catch (SQLException e) {
831                     // Shouldn't be needed unless we're debugging and interrupt the process
832                     Log.w(TAG, "Exception upgrading EmailProvider.db from 27 to 28 " + e);
833                 }
834                 oldVersion = 28;
835             }
836             if (oldVersion == 28) {
837                 try {
838                     db.execSQL("alter table " + Policy.TABLE_NAME
839                             + " add column " + Policy.PROTOCOL_POLICIES_ENFORCED + " text;");
840                     db.execSQL("alter table " + Policy.TABLE_NAME
841                             + " add column " + Policy.PROTOCOL_POLICIES_UNSUPPORTED + " text;");
842                 } catch (SQLException e) {
843                     // Shouldn't be needed unless we're debugging and interrupt the process
844                     Log.w(TAG, "Exception upgrading EmailProvider.db from 28 to 29 " + e);
845                 }
846                 oldVersion = 29;
847             }
848             if (oldVersion == 29) {
849                 upgradeFromVersion29ToVersion30(db);
850                 oldVersion = 30;
851             }
852             if (oldVersion == 30) {
853                 try {
854                     db.execSQL("alter table " + Mailbox.TABLE_NAME
855                             + " add column " + Mailbox.UI_SYNC_STATUS + " integer;");
856                     db.execSQL("alter table " + Mailbox.TABLE_NAME
857                             + " add column " + Mailbox.UI_LAST_SYNC_RESULT + " integer;");
858                 } catch (SQLException e) {
859                     // Shouldn't be needed unless we're debugging and interrupt the process
860                     Log.w(TAG, "Exception upgrading EmailProvider.db from 30 to 31 " + e);
861                 }
862                 oldVersion = 31;
863             }
864             if (oldVersion == 31) {
865                 try {
866                     db.execSQL("alter table " + Mailbox.TABLE_NAME
867                             + " add column " + Mailbox.LAST_NOTIFIED_MESSAGE_KEY + " integer;");
868                     db.execSQL("alter table " + Mailbox.TABLE_NAME
869                             + " add column " + Mailbox.LAST_NOTIFIED_MESSAGE_COUNT + " integer;");
870                     db.execSQL("update Mailbox set " + Mailbox.LAST_NOTIFIED_MESSAGE_KEY +
871                             "=0 where " + Mailbox.LAST_NOTIFIED_MESSAGE_KEY + " IS NULL");
872                     db.execSQL("update Mailbox set " + Mailbox.LAST_NOTIFIED_MESSAGE_COUNT +
873                             "=0 where " + Mailbox.LAST_NOTIFIED_MESSAGE_COUNT + " IS NULL");
874                 } catch (SQLException e) {
875                     // Shouldn't be needed unless we're debugging and interrupt the process
876                     Log.w(TAG, "Exception upgrading EmailProvider.db from 31 to 32 " + e);
877                 }
878                 oldVersion = 32;
879             }
880             if (oldVersion == 32) {
881                 try {
882                     db.execSQL("alter table " + Attachment.TABLE_NAME
883                             + " add column " + Attachment.UI_STATE + " integer;");
884                     db.execSQL("alter table " + Attachment.TABLE_NAME
885                             + " add column " + Attachment.UI_DESTINATION + " integer;");
886                     db.execSQL("alter table " + Attachment.TABLE_NAME
887                             + " add column " + Attachment.UI_DOWNLOADED_SIZE + " integer;");
888                 } catch (SQLException e) {
889                     // Shouldn't be needed unless we're debugging and interrupt the process
890                     Log.w(TAG, "Exception upgrading EmailProvider.db from 32 to 33 " + e);
891                 }
892                 oldVersion = 33;
893             }
894             if (oldVersion == 33) {
895                 try {
896                     db.execSQL("alter table " + Mailbox.TABLE_NAME
897                             + " add column " + MailboxColumns.TOTAL_COUNT + " integer;");
898                 } catch (SQLException e) {
899                     // Shouldn't be needed unless we're debugging and interrupt the process
900                     Log.w(TAG, "Exception upgrading EmailProvider.db from 33 to 34 " + e);
901                 }
902                 oldVersion = 34;
903             }
904             if (oldVersion == 34) {
905                 oldVersion = 35;
906             }
907             if (oldVersion == 35 || oldVersion == 36) {
908                 oldVersion = 37;
909             }
910             if (oldVersion == 37) {
911                 try {
912                     db.execSQL("alter table " + Message.TABLE_NAME
913                             + " add column " + MessageColumns.THREAD_TOPIC + " text;");
914                 } catch (SQLException e) {
915                     // Shouldn't be needed unless we're debugging and interrupt the process
916                     Log.w(TAG, "Exception upgrading EmailProvider.db from 37 to 38 " + e);
917                 }
918                 oldVersion = 38;
919             }
920             if (oldVersion == 38) {
921                 try {
922                     db.execSQL("alter table " + Message.DELETED_TABLE_NAME
923                             + " add column " + MessageColumns.THREAD_TOPIC + " text;");
924                     db.execSQL("alter table " + Message.UPDATED_TABLE_NAME
925                             + " add column " + MessageColumns.THREAD_TOPIC + " text;");
926                 } catch (SQLException e) {
927                     // Shouldn't be needed unless we're debugging and interrupt the process
928                     Log.w(TAG, "Exception upgrading EmailProvider.db from 38 to 39 " + e);
929                 }
930                 oldVersion = 39;
931             }
932         }
933 
934         @Override
onOpen(SQLiteDatabase db)935         public void onOpen(SQLiteDatabase db) {
936         }
937     }
938 
939     @VisibleForTesting
940     @SuppressWarnings("deprecation")
convertPolicyFlagsToPolicyTable(SQLiteDatabase db)941     static void convertPolicyFlagsToPolicyTable(SQLiteDatabase db) {
942         Cursor c = db.query(Account.TABLE_NAME,
943                 new String[] {EmailContent.RECORD_ID /*0*/, AccountColumns.SECURITY_FLAGS /*1*/},
944                 AccountColumns.SECURITY_FLAGS + ">0", null, null, null, null);
945         ContentValues cv = new ContentValues();
946         String[] args = new String[1];
947         while (c.moveToNext()) {
948             long securityFlags = c.getLong(1 /*SECURITY_FLAGS*/);
949             Policy policy = LegacyPolicySet.flagsToPolicy(securityFlags);
950             long policyId = db.insert(Policy.TABLE_NAME, null, policy.toContentValues());
951             cv.put(AccountColumns.POLICY_KEY, policyId);
952             cv.putNull(AccountColumns.SECURITY_FLAGS);
953             args[0] = Long.toString(c.getLong(0 /*RECORD_ID*/));
954             db.update(Account.TABLE_NAME, cv, EmailContent.RECORD_ID + "=?", args);
955         }
956     }
957 
958     /** Upgrades the database from v17 to v18 */
959     @VisibleForTesting
upgradeFromVersion17ToVersion18(SQLiteDatabase db)960     static void upgradeFromVersion17ToVersion18(SQLiteDatabase db) {
961         // Copy the displayName column to the serverId column. In v18 of the database,
962         // we use the serverId for IMAP/POP3 mailboxes instead of overloading the
963         // display name.
964         //
965         // For posterity; this is the command we're executing:
966         //sqlite> UPDATE mailbox SET serverid=displayname WHERE mailbox._id in (
967         //        ...> SELECT mailbox._id FROM mailbox,account,hostauth WHERE
968         //        ...> (mailbox.parentkey isnull OR mailbox.parentkey=0) AND
969         //        ...> mailbox.accountkey=account._id AND
970         //        ...> account.hostauthkeyrecv=hostauth._id AND
971         //        ...> (hostauth.protocol='imap' OR hostauth.protocol='pop3'));
972         try {
973             db.execSQL(
974                     "UPDATE " + Mailbox.TABLE_NAME + " SET "
975                     + MailboxColumns.SERVER_ID + "=" + MailboxColumns.DISPLAY_NAME
976                     + " WHERE "
977                     + Mailbox.TABLE_NAME + "." + MailboxColumns.ID + " IN ( SELECT "
978                     + Mailbox.TABLE_NAME + "." + MailboxColumns.ID + " FROM "
979                     + Mailbox.TABLE_NAME + "," + Account.TABLE_NAME + ","
980                     + HostAuth.TABLE_NAME + " WHERE "
981                     + "("
982                     + Mailbox.TABLE_NAME + "." + MailboxColumns.PARENT_KEY + " isnull OR "
983                     + Mailbox.TABLE_NAME + "." + MailboxColumns.PARENT_KEY + "=0 "
984                     + ") AND "
985                     + Mailbox.TABLE_NAME + "." + MailboxColumns.ACCOUNT_KEY + "="
986                     + Account.TABLE_NAME + "." + AccountColumns.ID + " AND "
987                     + Account.TABLE_NAME + "." + AccountColumns.HOST_AUTH_KEY_RECV + "="
988                     + HostAuth.TABLE_NAME + "." + HostAuthColumns.ID + " AND ( "
989                     + HostAuth.TABLE_NAME + "." + HostAuthColumns.PROTOCOL + "='imap' OR "
990                     + HostAuth.TABLE_NAME + "." + HostAuthColumns.PROTOCOL + "='pop3' ) )");
991         } catch (SQLException e) {
992             // Shouldn't be needed unless we're debugging and interrupt the process
993             Log.w(TAG, "Exception upgrading EmailProvider.db from 17 to 18 " + e);
994         }
995         ContentCache.invalidateAllCaches();
996     }
997 
998     /**
999      * Upgrade the database from v21 to v22
1000      * This entails creating AccountManager accounts for all pop3 and imap accounts
1001      */
1002 
1003     private static final String[] V21_ACCOUNT_PROJECTION =
1004         new String[] {AccountColumns.HOST_AUTH_KEY_RECV, AccountColumns.EMAIL_ADDRESS};
1005     private static final int V21_ACCOUNT_RECV = 0;
1006     private static final int V21_ACCOUNT_EMAIL = 1;
1007 
1008     private static final String[] V21_HOSTAUTH_PROJECTION =
1009         new String[] {HostAuthColumns.PROTOCOL, HostAuthColumns.PASSWORD};
1010     private static final int V21_HOSTAUTH_PROTOCOL = 0;
1011     private static final int V21_HOSTAUTH_PASSWORD = 1;
1012 
createAccountManagerAccount(Context context, String login, String password)1013     static private void createAccountManagerAccount(Context context, String login,
1014             String password) {
1015         AccountManager accountManager = AccountManager.get(context);
1016         android.accounts.Account amAccount =
1017             new android.accounts.Account(login, AccountManagerTypes.TYPE_POP_IMAP);
1018         accountManager.addAccountExplicitly(amAccount, password, null);
1019         ContentResolver.setIsSyncable(amAccount, EmailContent.AUTHORITY, 1);
1020         ContentResolver.setSyncAutomatically(amAccount, EmailContent.AUTHORITY, true);
1021         ContentResolver.setIsSyncable(amAccount, ContactsContract.AUTHORITY, 0);
1022         ContentResolver.setIsSyncable(amAccount, CalendarProviderStub.AUTHORITY, 0);
1023     }
1024 
1025     @VisibleForTesting
upgradeFromVersion21ToVersion22(SQLiteDatabase db, Context accountManagerContext)1026     static void upgradeFromVersion21ToVersion22(SQLiteDatabase db, Context accountManagerContext) {
1027         try {
1028             // Loop through accounts, looking for pop/imap accounts
1029             Cursor accountCursor = db.query(Account.TABLE_NAME, V21_ACCOUNT_PROJECTION, null,
1030                     null, null, null, null);
1031             try {
1032                 String[] hostAuthArgs = new String[1];
1033                 while (accountCursor.moveToNext()) {
1034                     hostAuthArgs[0] = accountCursor.getString(V21_ACCOUNT_RECV);
1035                     // Get the "receive" HostAuth for this account
1036                     Cursor hostAuthCursor = db.query(HostAuth.TABLE_NAME,
1037                             V21_HOSTAUTH_PROJECTION, HostAuth.RECORD_ID + "=?", hostAuthArgs,
1038                             null, null, null);
1039                     try {
1040                         if (hostAuthCursor.moveToFirst()) {
1041                             String protocol = hostAuthCursor.getString(V21_HOSTAUTH_PROTOCOL);
1042                             // If this is a pop3 or imap account, create the account manager account
1043                             if (HostAuth.SCHEME_IMAP.equals(protocol) ||
1044                                     HostAuth.SCHEME_POP3.equals(protocol)) {
1045                                 if (Email.DEBUG) {
1046                                     Log.d(TAG, "Create AccountManager account for " + protocol +
1047                                             "account: " +
1048                                             accountCursor.getString(V21_ACCOUNT_EMAIL));
1049                                 }
1050                                 createAccountManagerAccount(accountManagerContext,
1051                                         accountCursor.getString(V21_ACCOUNT_EMAIL),
1052                                         hostAuthCursor.getString(V21_HOSTAUTH_PASSWORD));
1053                             // If an EAS account, make Email sync automatically (equivalent of
1054                             // checking the "Sync Email" box in settings
1055                             } else if (HostAuth.SCHEME_EAS.equals(protocol)) {
1056                                 android.accounts.Account amAccount =
1057                                         new android.accounts.Account(
1058                                                 accountCursor.getString(V21_ACCOUNT_EMAIL),
1059                                                 AccountManagerTypes.TYPE_EXCHANGE);
1060                                 ContentResolver.setIsSyncable(amAccount, EmailContent.AUTHORITY, 1);
1061                                 ContentResolver.setSyncAutomatically(amAccount,
1062                                         EmailContent.AUTHORITY, true);
1063 
1064                             }
1065                         }
1066                     } finally {
1067                         hostAuthCursor.close();
1068                     }
1069                 }
1070             } finally {
1071                 accountCursor.close();
1072             }
1073         } catch (SQLException e) {
1074             // Shouldn't be needed unless we're debugging and interrupt the process
1075             Log.w(TAG, "Exception upgrading EmailProvider.db from 20 to 21 " + e);
1076         }
1077     }
1078 
1079     /** Upgrades the database from v22 to v23 */
upgradeFromVersion22ToVersion23(SQLiteDatabase db)1080     private static void upgradeFromVersion22ToVersion23(SQLiteDatabase db) {
1081         try {
1082             db.execSQL("alter table " + Mailbox.TABLE_NAME
1083                     + " add column " + Mailbox.LAST_TOUCHED_TIME + " integer default 0;");
1084         } catch (SQLException e) {
1085             // Shouldn't be needed unless we're debugging and interrupt the process
1086             Log.w(TAG, "Exception upgrading EmailProvider.db from 22 to 23 " + e);
1087         }
1088     }
1089 
1090     /** Adds in a column for information about a client certificate to use. */
upgradeFromVersion23ToVersion24(SQLiteDatabase db)1091     private static void upgradeFromVersion23ToVersion24(SQLiteDatabase db) {
1092         try {
1093             db.execSQL("alter table " + HostAuth.TABLE_NAME
1094                     + " add column " + HostAuth.CLIENT_CERT_ALIAS + " text;");
1095         } catch (SQLException e) {
1096             // Shouldn't be needed unless we're debugging and interrupt the process
1097             Log.w(TAG, "Exception upgrading EmailProvider.db from 23 to 24 " + e);
1098         }
1099     }
1100 
1101     /** Upgrades the database from v24 to v25 by creating table for quick responses */
upgradeFromVersion24ToVersion25(SQLiteDatabase db)1102     private static void upgradeFromVersion24ToVersion25(SQLiteDatabase db) {
1103         try {
1104             createQuickResponseTable(db);
1105         } catch (SQLException e) {
1106             // Shouldn't be needed unless we're debugging and interrupt the process
1107             Log.w(TAG, "Exception upgrading EmailProvider.db from 24 to 25 " + e);
1108         }
1109     }
1110 
1111     private static final String[] V25_ACCOUNT_PROJECTION =
1112         new String[] {AccountColumns.ID, AccountColumns.FLAGS, AccountColumns.HOST_AUTH_KEY_RECV};
1113     private static final int V25_ACCOUNT_ID = 0;
1114     private static final int V25_ACCOUNT_FLAGS = 1;
1115     private static final int V25_ACCOUNT_RECV = 2;
1116 
1117     private static final String[] V25_HOSTAUTH_PROJECTION = new String[] {HostAuthColumns.PROTOCOL};
1118     private static final int V25_HOSTAUTH_PROTOCOL = 0;
1119 
1120     /** Upgrades the database from v25 to v26 by adding FLAG_SUPPORTS_SEARCH to IMAP accounts */
upgradeFromVersion25ToVersion26(SQLiteDatabase db)1121     private static void upgradeFromVersion25ToVersion26(SQLiteDatabase db) {
1122         try {
1123             // Loop through accounts, looking for imap accounts
1124             Cursor accountCursor = db.query(Account.TABLE_NAME, V25_ACCOUNT_PROJECTION, null,
1125                     null, null, null, null);
1126             ContentValues cv = new ContentValues();
1127             try {
1128                 String[] hostAuthArgs = new String[1];
1129                 while (accountCursor.moveToNext()) {
1130                     hostAuthArgs[0] = accountCursor.getString(V25_ACCOUNT_RECV);
1131                     // Get the "receive" HostAuth for this account
1132                     Cursor hostAuthCursor = db.query(HostAuth.TABLE_NAME,
1133                             V25_HOSTAUTH_PROJECTION, HostAuth.RECORD_ID + "=?", hostAuthArgs,
1134                             null, null, null);
1135                     try {
1136                         if (hostAuthCursor.moveToFirst()) {
1137                             String protocol = hostAuthCursor.getString(V25_HOSTAUTH_PROTOCOL);
1138                             // If this is an imap account, add the search flag
1139                             if (HostAuth.SCHEME_IMAP.equals(protocol)) {
1140                                 String id = accountCursor.getString(V25_ACCOUNT_ID);
1141                                 int flags = accountCursor.getInt(V25_ACCOUNT_FLAGS);
1142                                 cv.put(AccountColumns.FLAGS, flags | Account.FLAGS_SUPPORTS_SEARCH);
1143                                 db.update(Account.TABLE_NAME, cv, Account.RECORD_ID + "=?",
1144                                         new String[] {id});
1145                             }
1146                         }
1147                     } finally {
1148                         hostAuthCursor.close();
1149                     }
1150                 }
1151             } finally {
1152                 accountCursor.close();
1153             }
1154         } catch (SQLException e) {
1155             // Shouldn't be needed unless we're debugging and interrupt the process
1156             Log.w(TAG, "Exception upgrading EmailProvider.db from 25 to 26 " + e);
1157         }
1158     }
1159 
1160     /** Upgrades the database from v29 to v30 by updating all address fields in Message */
1161     private static final int[] ADDRESS_COLUMN_INDICES = new int[] {
1162         Message.CONTENT_BCC_LIST_COLUMN, Message.CONTENT_CC_LIST_COLUMN,
1163         Message.CONTENT_FROM_LIST_COLUMN, Message.CONTENT_REPLY_TO_COLUMN,
1164         Message.CONTENT_TO_LIST_COLUMN
1165     };
1166     private static final String[] ADDRESS_COLUMN_NAMES = new String[] {
1167         Message.BCC_LIST, Message.CC_LIST, Message.FROM_LIST, Message.REPLY_TO_LIST, Message.TO_LIST
1168     };
1169 
upgradeFromVersion29ToVersion30(SQLiteDatabase db)1170     private static void upgradeFromVersion29ToVersion30(SQLiteDatabase db) {
1171         try {
1172             // Loop through all messages, updating address columns to new format (CSV, RFC822)
1173             Cursor messageCursor = db.query(Message.TABLE_NAME, Message.CONTENT_PROJECTION, null,
1174                     null, null, null, null);
1175             ContentValues cv = new ContentValues();
1176             String[] whereArgs = new String[1];
1177             try {
1178                 while (messageCursor.moveToNext()) {
1179                     for (int i = 0; i < ADDRESS_COLUMN_INDICES.length; i++) {
1180                         Address[] addrs =
1181                                 Address.unpack(messageCursor.getString(ADDRESS_COLUMN_INDICES[i]));
1182                         cv.put(ADDRESS_COLUMN_NAMES[i], Address.pack(addrs));
1183                     }
1184                     whereArgs[0] = messageCursor.getString(Message.CONTENT_ID_COLUMN);
1185                     db.update(Message.TABLE_NAME, cv, WHERE_ID, whereArgs);
1186                 }
1187             } finally {
1188                 messageCursor.close();
1189             }
1190         } catch (SQLException e) {
1191             // Shouldn't be needed unless we're debugging and interrupt the process
1192             Log.w(TAG, "Exception upgrading EmailProvider.db from 29 to 30 " + e);
1193         }
1194     }
1195 
1196 }
1197