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