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