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