1 /*
2  * Copyright (C) 2015 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 package com.android.providers.contacts;
17 
18 import android.annotation.Nullable;
19 import android.content.ContentValues;
20 import android.content.Context;
21 import android.content.SharedPreferences;
22 import android.database.Cursor;
23 import android.database.DatabaseUtils;
24 import android.database.SQLException;
25 import android.database.sqlite.SQLiteCantOpenDatabaseException;
26 import android.database.sqlite.SQLiteDatabase;
27 import android.database.sqlite.SQLiteOpenHelper;
28 import android.preference.PreferenceManager;
29 import android.provider.CallLog.Calls;
30 import android.provider.VoicemailContract;
31 import android.provider.VoicemailContract.Status;
32 import android.provider.VoicemailContract.Voicemails;
33 import android.telephony.SubscriptionInfo;
34 import android.telephony.SubscriptionManager;
35 import android.text.TextUtils;
36 import android.util.ArraySet;
37 import android.util.Log;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.providers.contacts.util.PhoneAccountHandleMigrationUtils;
41 import com.android.providers.contacts.util.PropertyUtils;
42 
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Map;
46 
47 /**
48  * SQLite database (helper) for {@link CallLogProvider} and {@link VoicemailContentProvider}.
49  */
50 public class CallLogDatabaseHelper {
51     private static final String TAG = "CallLogDatabaseHelper";
52 
53     @VisibleForTesting
54     static final int DATABASE_VERSION = 12;
55 
56     private static final boolean DEBUG = false; // DON'T SUBMIT WITH TRUE
57 
58     private static final String DATABASE_NAME = "calllog.db";
59 
60     private static final String SHADOW_DATABASE_NAME = "calllog_shadow.db";
61 
62     private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
63 
64     private static CallLogDatabaseHelper sInstance;
65 
66     /** Instance for the "shadow" provider. */
67     private static CallLogDatabaseHelper sInstanceForShadow;
68 
69     private final Context mContext;
70 
71     private final OpenHelper mOpenHelper;
72 
73     @VisibleForTesting
74     final PhoneAccountHandleMigrationUtils mPhoneAccountHandleMigrationUtils;
75 
76     public interface Tables {
77         String CALLS = "calls";
78         String VOICEMAIL_STATUS = "voicemail_status";
79     }
80 
81     public interface DbProperties {
82         String CALL_LOG_LAST_SYNCED = "call_log_last_synced";
83         String CALL_LOG_LAST_SYNCED_FOR_SHADOW = "call_log_last_synced_for_shadow";
84         String DATA_MIGRATED = "migrated";
85     }
86 
87     /**
88      * Constants used in the contacts DB helper, which are needed for migration.
89      *
90      * DO NOT CHANCE ANY OF THE CONSTANTS.
91      */
92     public interface LegacyConstants {
93         /** Table name used in the contacts DB.*/
94         String CALLS_LEGACY = "calls";
95 
96         /** Table name used in the contacts DB.*/
97         String VOICEMAIL_STATUS_LEGACY = "voicemail_status";
98 
99         /** Prop name used in the contacts DB.*/
100         String CALL_LOG_LAST_SYNCED_LEGACY = "call_log_last_synced";
101     }
102 
103     @VisibleForTesting
104     public class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)105         public OpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,
106                 int version) {
107             super(context, name, factory, version);
108             // Memory optimization - close idle connections after 30s of inactivity
109             setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
110         }
111 
112         @Override
onCreate(SQLiteDatabase db)113         public void onCreate(SQLiteDatabase db) {
114             if (DEBUG) {
115                 Log.d(TAG, "onCreate");
116             }
117 
118             PropertyUtils.createPropertiesTable(db);
119 
120             // *** NOTE ABOUT CHANGING THE DB SCHEMA ***
121             //
122             // The CALLS and VOICEMAIL_STATUS table used to be in the contacts2.db.  So we need to
123             // migrate from these legacy tables, if exist, after creating the calllog DB, which is
124             // done in migrateFromLegacyTables().
125             //
126             // This migration is slightly different from a regular upgrade step, because it's always
127             // performed from the legacy schema (of the latest version -- because the migration
128             // source is always the latest DB after all the upgrade steps) to the *latest* schema
129             // at once.
130             //
131             // This means certain kind of changes are not doable without changing the
132             // migration logic.  For example, if you rename a column in the DB, the migration step
133             // will need to be updated to handle the column name change.
134 
135             db.execSQL("CREATE TABLE " + Tables.CALLS + " (" +
136                     Calls._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
137                     Calls.NUMBER + " TEXT," +
138                     Calls.NUMBER_PRESENTATION + " INTEGER NOT NULL DEFAULT " +
139                     Calls.PRESENTATION_ALLOWED + "," +
140                     Calls.POST_DIAL_DIGITS + " TEXT NOT NULL DEFAULT ''," +
141                     Calls.VIA_NUMBER + " TEXT NOT NULL DEFAULT ''," +
142                     Calls.DATE + " INTEGER," +
143                     Calls.DURATION + " INTEGER," +
144                     Calls.DATA_USAGE + " INTEGER," +
145                     Calls.TYPE + " INTEGER," +
146                     Calls.FEATURES + " INTEGER NOT NULL DEFAULT 0," +
147                     Calls.PHONE_ACCOUNT_COMPONENT_NAME + " TEXT," +
148                     Calls.PHONE_ACCOUNT_ID + " TEXT," +
149                     Calls.PHONE_ACCOUNT_ADDRESS + " TEXT," +
150                     Calls.PHONE_ACCOUNT_HIDDEN + " INTEGER NOT NULL DEFAULT 0," +
151                     Calls.SUB_ID + " INTEGER DEFAULT -1," +
152                     Calls.NEW + " INTEGER," +
153                     Calls.CACHED_NAME + " TEXT," +
154                     Calls.CACHED_NUMBER_TYPE + " INTEGER," +
155                     Calls.CACHED_NUMBER_LABEL + " TEXT," +
156                     Calls.COUNTRY_ISO + " TEXT," +
157                     Calls.VOICEMAIL_URI + " TEXT," +
158                     Calls.IS_READ + " INTEGER," +
159                     Calls.GEOCODED_LOCATION + " TEXT," +
160                     Calls.CACHED_LOOKUP_URI + " TEXT," +
161                     Calls.CACHED_MATCHED_NUMBER + " TEXT," +
162                     Calls.CACHED_NORMALIZED_NUMBER + " TEXT," +
163                     Calls.CACHED_PHOTO_ID + " INTEGER NOT NULL DEFAULT 0," +
164                     Calls.CACHED_PHOTO_URI + " TEXT," +
165                     Calls.CACHED_FORMATTED_NUMBER + " TEXT," +
166                     Calls.ADD_FOR_ALL_USERS + " INTEGER NOT NULL DEFAULT 1," +
167                     Calls.LAST_MODIFIED + " INTEGER DEFAULT 0," +
168                     Calls.CALL_SCREENING_COMPONENT_NAME + " TEXT," +
169                     Calls.CALL_SCREENING_APP_NAME + " TEXT," +
170                     Calls.BLOCK_REASON + " INTEGER NOT NULL DEFAULT 0," +
171                     Calls.MISSED_REASON + " INTEGER NOT NULL DEFAULT 0," +
172                     Calls.PRIORITY + " INTEGER NOT NULL DEFAULT 0," +
173                     Calls.SUBJECT + " TEXT," +
174                     Calls.LOCATION + " TEXT," +
175                     Calls.COMPOSER_PHOTO_URI + " TEXT," +
176                     Calls.IS_PHONE_ACCOUNT_MIGRATION_PENDING + " INTEGER NOT NULL DEFAULT 0," +
177                     Calls.IS_BUSINESS_CALL + " INTEGER NOT NULL DEFAULT 0," +
178                     Calls.ASSERTED_DISPLAY_NAME + " TEXT," +
179 
180                     Voicemails._DATA + " TEXT," +
181                     Voicemails.HAS_CONTENT + " INTEGER," +
182                     Voicemails.MIME_TYPE + " TEXT," +
183                     Voicemails.SOURCE_DATA + " TEXT," +
184                     Voicemails.SOURCE_PACKAGE + " TEXT," +
185                     Voicemails.TRANSCRIPTION + " TEXT," +
186                     Voicemails.TRANSCRIPTION_STATE + " INTEGER NOT NULL DEFAULT 0," +
187                     Voicemails.STATE + " INTEGER," +
188                     Voicemails.DIRTY + " INTEGER NOT NULL DEFAULT 0," +
189                     Voicemails.DELETED + " INTEGER NOT NULL DEFAULT 0," +
190                     Voicemails.BACKED_UP + " INTEGER NOT NULL DEFAULT 0," +
191                     Voicemails.RESTORED + " INTEGER NOT NULL DEFAULT 0," +
192                     Voicemails.ARCHIVED + " INTEGER NOT NULL DEFAULT 0," +
193                     Voicemails.IS_OMTP_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0" +
194                     ");");
195 
196             db.execSQL("CREATE TABLE " + Tables.VOICEMAIL_STATUS + " (" +
197                     VoicemailContract.Status._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
198                     VoicemailContract.Status.SOURCE_PACKAGE + " TEXT NOT NULL," +
199                     VoicemailContract.Status.PHONE_ACCOUNT_COMPONENT_NAME + " TEXT," +
200                     VoicemailContract.Status.PHONE_ACCOUNT_ID + " TEXT," +
201                     VoicemailContract.Status.SETTINGS_URI + " TEXT," +
202                     VoicemailContract.Status.VOICEMAIL_ACCESS_URI + " TEXT," +
203                     VoicemailContract.Status.CONFIGURATION_STATE + " INTEGER," +
204                     VoicemailContract.Status.DATA_CHANNEL_STATE + " INTEGER," +
205                     VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE + " INTEGER," +
206                     VoicemailContract.Status.QUOTA_OCCUPIED + " INTEGER DEFAULT -1," +
207                     VoicemailContract.Status.QUOTA_TOTAL + " INTEGER DEFAULT -1," +
208                     VoicemailContract.Status.SOURCE_TYPE + " TEXT" +
209                     ");");
210 
211         }
212 
213         @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)214         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
215             if (DEBUG) {
216                 Log.d(TAG, "onUpgrade");
217             }
218 
219             if (oldVersion < 2) {
220                 upgradeToVersion2(db);
221             }
222 
223             if (oldVersion < 3) {
224                 upgradeToVersion3(db);
225             }
226 
227             if (oldVersion < 4) {
228                 upgradeToVersion4(db);
229             }
230 
231             if (oldVersion < 5) {
232                 upgradeToVersion5(db);
233             }
234 
235             if (oldVersion < 6) {
236                 upgradeToVersion6(db);
237             }
238 
239             if (oldVersion < 7) {
240                 upgradeToVersion7(db);
241             }
242 
243             if (oldVersion < 8) {
244                 upgradetoVersion8(db);
245             }
246 
247             if (oldVersion < 9) {
248                 upgradeToVersion9(db);
249             }
250 
251             if (oldVersion < 10) {
252                 upgradeToVersion10(db);
253             }
254 
255             if (oldVersion < 11) {
256                 upgradeToVersion11(db);
257             }
258 
259             if (oldVersion < 12) {
260                 upgradeToVersion12(db);
261             }
262         }
263 
264         @Override
onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)265         public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
266             // Ignore
267         }
268     }
269 
270     @VisibleForTesting
CallLogDatabaseHelper(Context context, String databaseName)271     CallLogDatabaseHelper(Context context, String databaseName) {
272         mContext = context;
273         mPhoneAccountHandleMigrationUtils = new PhoneAccountHandleMigrationUtils(
274                 context, PhoneAccountHandleMigrationUtils.TYPE_CALL_LOG);
275         mOpenHelper = new OpenHelper(mContext, databaseName, /* factory=*/ null, DATABASE_VERSION);
276     }
277 
getInstance(Context context)278     public static synchronized CallLogDatabaseHelper getInstance(Context context) {
279         if (sInstance == null) {
280             sInstance = new CallLogDatabaseHelper(context, DATABASE_NAME);
281         }
282         return sInstance;
283     }
284 
getInstanceForShadow(Context context)285     public static synchronized CallLogDatabaseHelper getInstanceForShadow(Context context) {
286         if (sInstanceForShadow == null) {
287             // Shadow provider is always encryption-aware.
288             sInstanceForShadow = new CallLogDatabaseHelper(
289                     context.createDeviceProtectedStorageContext(), SHADOW_DATABASE_NAME);
290         }
291         return sInstanceForShadow;
292     }
293 
getReadableDatabase()294     public SQLiteDatabase getReadableDatabase() {
295         return mOpenHelper.getReadableDatabase();
296     }
297 
getWritableDatabase()298     public SQLiteDatabase getWritableDatabase() {
299         return mOpenHelper.getWritableDatabase();
300     }
301 
getProperty(String key, String defaultValue)302     public String getProperty(String key, String defaultValue) {
303         return PropertyUtils.getProperty(getReadableDatabase(), key, defaultValue);
304     }
305 
setProperty(String key, String value)306     public void setProperty(String key, String value) {
307         PropertyUtils.setProperty(getWritableDatabase(), key, value);
308     }
309 
310     /**
311      * Updates phone account migration pending status, indicating if there is any phone account
312      * handle that need to migrate. Called in CallLogProvider.
313      */
updatePhoneAccountHandleMigrationPendingStatus()314     void updatePhoneAccountHandleMigrationPendingStatus() {
315         mPhoneAccountHandleMigrationUtils.updatePhoneAccountHandleMigrationPendingStatus(
316                 getWritableDatabase());
317     }
318 
319     /**
320      * Migrate all the pending phone account handles based on the given iccId and subId. Used
321      * by CallLogProvider.
322      */
migratePendingPhoneAccountHandles(String iccId, String subId)323     void migratePendingPhoneAccountHandles(String iccId, String subId) {
324         mPhoneAccountHandleMigrationUtils.migratePendingPhoneAccountHandles(
325                 iccId, subId, getWritableDatabase());
326     }
327 
328     /**
329      * Try to migrate any PhoneAccountId to SubId from IccId. Used by CallLogProvider.
330      */
migrateIccIdToSubId()331     void migrateIccIdToSubId() {
332         mPhoneAccountHandleMigrationUtils.migrateIccIdToSubId(getWritableDatabase());
333     }
334 
335     /**
336      * Add the {@link Calls.VIA_NUMBER} Column to the CallLog Database.
337      */
upgradeToVersion2(SQLiteDatabase db)338     private void upgradeToVersion2(SQLiteDatabase db) {
339         db.execSQL("ALTER TABLE " + Tables.CALLS + " ADD " + Calls.VIA_NUMBER +
340                 " TEXT NOT NULL DEFAULT ''");
341     }
342 
343     /**
344      * Add the {@link Status.SOURCE_TYPE} Column to the VoicemailStatus Database.
345      */
upgradeToVersion3(SQLiteDatabase db)346     private void upgradeToVersion3(SQLiteDatabase db) {
347         db.execSQL("ALTER TABLE " + Tables.VOICEMAIL_STATUS + " ADD " + Status.SOURCE_TYPE +
348                 " TEXT");
349     }
350 
351     /**
352      * Add {@link Voicemails.BACKED_UP} {@link Voicemails.ARCHIVE} {@link
353      * Voicemails.IS_OMTP_VOICEMAIL} column to the CallLog database.
354      */
upgradeToVersion4(SQLiteDatabase db)355     private void upgradeToVersion4(SQLiteDatabase db) {
356         db.execSQL("ALTER TABLE calls ADD backed_up INTEGER NOT NULL DEFAULT 0");
357         db.execSQL("ALTER TABLE calls ADD restored INTEGER NOT NULL DEFAULT 0");
358         db.execSQL("ALTER TABLE calls ADD archived INTEGER NOT NULL DEFAULT 0");
359         db.execSQL("ALTER TABLE calls ADD is_omtp_voicemail INTEGER NOT NULL DEFAULT 0");
360     }
361 
362     /**
363      * Add {@link Voicemails.TRANSCRIPTION_STATE} column to the CallLog database.
364      */
upgradeToVersion5(SQLiteDatabase db)365     private void upgradeToVersion5(SQLiteDatabase db) {
366         db.execSQL("ALTER TABLE calls ADD transcription_state INTEGER NOT NULL DEFAULT 0");
367     }
368 
369     /**
370      * Add {@link CallLog.Calls#CALL_SCREENING_COMPONENT_NAME}
371      * {@link CallLog.Calls#CALL_SCREENING_APP_NAME}
372      * {@link CallLog.Calls#BLOCK_REASON} column to the CallLog database.
373      */
upgradeToVersion6(SQLiteDatabase db)374     private void upgradeToVersion6(SQLiteDatabase db) {
375         db.execSQL("ALTER TABLE calls ADD call_screening_component_name TEXT");
376         db.execSQL("ALTER TABLE calls ADD call_screening_app_name TEXT");
377         db.execSQL("ALTER TABLE calls ADD block_reason INTEGER NOT NULL DEFAULT 0");
378     }
379 
380     /**
381      * Add {@code android.telecom.CallIdentification} columns; these are destined to be removed
382      * in {@link #upgradetoVersion8(SQLiteDatabase)}.
383      * @param db DB to upgrade
384      */
upgradeToVersion7(SQLiteDatabase db)385     private void upgradeToVersion7(SQLiteDatabase db) {
386         db.execSQL("ALTER TABLE calls ADD call_id_package_name TEXT NULL");
387         db.execSQL("ALTER TABLE calls ADD call_id_app_name TEXT NULL");
388         db.execSQL("ALTER TABLE calls ADD call_id_name TEXT NULL");
389         db.execSQL("ALTER TABLE calls ADD call_id_description TEXT NULL");
390         db.execSQL("ALTER TABLE calls ADD call_id_details TEXT NULL");
391         db.execSQL("ALTER TABLE calls ADD call_id_nuisance_confidence INTEGER NULL");
392     }
393 
394     /**
395      * Remove the {@code android.telecom.CallIdentification} column.
396      * @param db DB to upgrade
397      */
upgradetoVersion8(SQLiteDatabase db)398     private void upgradetoVersion8(SQLiteDatabase db) {
399         db.beginTransaction();
400         try {
401             String oldTable = Tables.CALLS + "_old";
402             // SQLite3 doesn't support altering a column name, so we'll rename the old calls table..
403             db.execSQL("ALTER TABLE calls RENAME TO " + oldTable);
404 
405             // ... create a new one (yes, this seems similar to what is in onCreate, but we can't
406             // assume that one won't change in the future) ...
407             db.execSQL("CREATE TABLE " + Tables.CALLS + " (" +
408                     Calls._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
409                     Calls.NUMBER + " TEXT," +
410                     Calls.NUMBER_PRESENTATION + " INTEGER NOT NULL DEFAULT " +
411                     Calls.PRESENTATION_ALLOWED + "," +
412                     Calls.POST_DIAL_DIGITS + " TEXT NOT NULL DEFAULT ''," +
413                     Calls.VIA_NUMBER + " TEXT NOT NULL DEFAULT ''," +
414                     Calls.DATE + " INTEGER," +
415                     Calls.DURATION + " INTEGER," +
416                     Calls.DATA_USAGE + " INTEGER," +
417                     Calls.TYPE + " INTEGER," +
418                     Calls.FEATURES + " INTEGER NOT NULL DEFAULT 0," +
419                     Calls.PHONE_ACCOUNT_COMPONENT_NAME + " TEXT," +
420                     Calls.PHONE_ACCOUNT_ID + " TEXT," +
421                     Calls.PHONE_ACCOUNT_ADDRESS + " TEXT," +
422                     Calls.PHONE_ACCOUNT_HIDDEN + " INTEGER NOT NULL DEFAULT 0," +
423                     Calls.SUB_ID + " INTEGER DEFAULT -1," +
424                     Calls.NEW + " INTEGER," +
425                     Calls.CACHED_NAME + " TEXT," +
426                     Calls.CACHED_NUMBER_TYPE + " INTEGER," +
427                     Calls.CACHED_NUMBER_LABEL + " TEXT," +
428                     Calls.COUNTRY_ISO + " TEXT," +
429                     Calls.VOICEMAIL_URI + " TEXT," +
430                     Calls.IS_READ + " INTEGER," +
431                     Calls.GEOCODED_LOCATION + " TEXT," +
432                     Calls.CACHED_LOOKUP_URI + " TEXT," +
433                     Calls.CACHED_MATCHED_NUMBER + " TEXT," +
434                     Calls.CACHED_NORMALIZED_NUMBER + " TEXT," +
435                     Calls.CACHED_PHOTO_ID + " INTEGER NOT NULL DEFAULT 0," +
436                     Calls.CACHED_PHOTO_URI + " TEXT," +
437                     Calls.CACHED_FORMATTED_NUMBER + " TEXT," +
438                     Calls.ADD_FOR_ALL_USERS + " INTEGER NOT NULL DEFAULT 1," +
439                     Calls.LAST_MODIFIED + " INTEGER DEFAULT 0," +
440                     Calls.CALL_SCREENING_COMPONENT_NAME + " TEXT," +
441                     Calls.CALL_SCREENING_APP_NAME + " TEXT," +
442                     Calls.BLOCK_REASON + " INTEGER NOT NULL DEFAULT 0," +
443 
444                     Voicemails._DATA + " TEXT," +
445                     Voicemails.HAS_CONTENT + " INTEGER," +
446                     Voicemails.MIME_TYPE + " TEXT," +
447                     Voicemails.SOURCE_DATA + " TEXT," +
448                     Voicemails.SOURCE_PACKAGE + " TEXT," +
449                     Voicemails.TRANSCRIPTION + " TEXT," +
450                     Voicemails.TRANSCRIPTION_STATE + " INTEGER NOT NULL DEFAULT 0," +
451                     Voicemails.STATE + " INTEGER," +
452                     Voicemails.DIRTY + " INTEGER NOT NULL DEFAULT 0," +
453                     Voicemails.DELETED + " INTEGER NOT NULL DEFAULT 0," +
454                     Voicemails.BACKED_UP + " INTEGER NOT NULL DEFAULT 0," +
455                     Voicemails.RESTORED + " INTEGER NOT NULL DEFAULT 0," +
456                     Voicemails.ARCHIVED + " INTEGER NOT NULL DEFAULT 0," +
457                     Voicemails.IS_OMTP_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0" +
458                     ");");
459 
460             String allTheColumns = Calls._ID + ", " +
461                     Calls.NUMBER + ", " +
462                     Calls.NUMBER_PRESENTATION + ", " +
463                     Calls.POST_DIAL_DIGITS + ", " +
464                     Calls.VIA_NUMBER + ", " +
465                     Calls.DATE + ", " +
466                     Calls.DURATION + ", " +
467                     Calls.DATA_USAGE + ", " +
468                     Calls.TYPE + ", " +
469                     Calls.FEATURES + ", " +
470                     Calls.PHONE_ACCOUNT_COMPONENT_NAME + ", " +
471                     Calls.PHONE_ACCOUNT_ID + ", " +
472                     Calls.PHONE_ACCOUNT_ADDRESS + ", " +
473                     Calls.PHONE_ACCOUNT_HIDDEN + ", " +
474                     Calls.SUB_ID + ", " +
475                     Calls.NEW + ", " +
476                     Calls.CACHED_NAME + ", " +
477                     Calls.CACHED_NUMBER_TYPE + ", " +
478                     Calls.CACHED_NUMBER_LABEL + ", " +
479                     Calls.COUNTRY_ISO + ", " +
480                     Calls.VOICEMAIL_URI + ", " +
481                     Calls.IS_READ + ", " +
482                     Calls.GEOCODED_LOCATION + ", " +
483                     Calls.CACHED_LOOKUP_URI + ", " +
484                     Calls.CACHED_MATCHED_NUMBER + ", " +
485                     Calls.CACHED_NORMALIZED_NUMBER + ", " +
486                     Calls.CACHED_PHOTO_ID + ", " +
487                     Calls.CACHED_PHOTO_URI + ", " +
488                     Calls.CACHED_FORMATTED_NUMBER + ", " +
489                     Calls.ADD_FOR_ALL_USERS + ", " +
490                     Calls.LAST_MODIFIED + ", " +
491                     Calls.CALL_SCREENING_COMPONENT_NAME + ", " +
492                     Calls.CALL_SCREENING_APP_NAME + ", " +
493                     Calls.BLOCK_REASON + ", " +
494 
495                     Voicemails._DATA + ", " +
496                     Voicemails.HAS_CONTENT + ", " +
497                     Voicemails.MIME_TYPE + ", " +
498                     Voicemails.SOURCE_DATA + ", " +
499                     Voicemails.SOURCE_PACKAGE + ", " +
500                     Voicemails.TRANSCRIPTION + ", " +
501                     Voicemails.TRANSCRIPTION_STATE + ", " +
502                     Voicemails.STATE + ", " +
503                     Voicemails.DIRTY + ", " +
504                     Voicemails.DELETED + ", " +
505                     Voicemails.BACKED_UP + ", " +
506                     Voicemails.RESTORED + ", " +
507                     Voicemails.ARCHIVED + ", " +
508                     Voicemails.IS_OMTP_VOICEMAIL;
509 
510             // .. so we insert into the new table all the values from the old table ...
511             db.execSQL("INSERT INTO " + Tables.CALLS + " (" +
512                     allTheColumns + ") SELECT " +
513                     allTheColumns + " FROM " + oldTable);
514 
515             // .. and drop the old table we renamed.
516             db.execSQL("DROP TABLE " + oldTable);
517 
518             db.setTransactionSuccessful();
519         } finally {
520             db.endTransaction();
521         }
522     }
523 
upgradeToVersion9(SQLiteDatabase db)524     private void upgradeToVersion9(SQLiteDatabase db) {
525         db.execSQL("ALTER TABLE calls ADD missed_reason INTEGER NOT NULL DEFAULT 0");
526     }
527 
upgradeToVersion10(SQLiteDatabase db)528     private void upgradeToVersion10(SQLiteDatabase db) {
529         db.execSQL("ALTER TABLE calls ADD priority INTEGER NOT NULL DEFAULT 0");
530         db.execSQL("ALTER TABLE calls ADD subject TEXT");
531         db.execSQL("ALTER TABLE calls ADD location TEXT");
532         db.execSQL("ALTER TABLE calls ADD composer_photo_uri TEXT");
533     }
534 
upgradeToVersion11(SQLiteDatabase db)535     private void upgradeToVersion11(SQLiteDatabase db) {
536         // Create colums for IS_PHONE_ACCOUNT_MIGRATION_PENDING
537         db.execSQL("ALTER TABLE calls ADD is_call_log_phone_account_migration_pending"
538                 + " INTEGER NOT NULL DEFAULT 0");
539         mPhoneAccountHandleMigrationUtils.markAllTelephonyPhoneAccountsPendingMigration(db);
540         mPhoneAccountHandleMigrationUtils.migrateIccIdToSubId(db);
541     }
542 
upgradeToVersion12(SQLiteDatabase db)543     private void upgradeToVersion12(SQLiteDatabase db) {
544         try {
545             db.execSQL("ALTER TABLE calls ADD is_business_call INTEGER NOT NULL DEFAULT 0");
546             db.execSQL("ALTER TABLE calls ADD asserted_display_name TEXT");
547         } catch (SQLException ignore) {
548             Log.i(TAG, String.format("upgradeToVersion12: SQLException occurred e=[%s]", ignore));
549         }
550     }
551 
552     @VisibleForTesting
tableExists(SQLiteDatabase db, String table)553     static boolean tableExists(SQLiteDatabase db, String table) {
554         return DatabaseUtils.longForQuery(db,
555                 "select count(*) from sqlite_master where type='table' and name=?",
556                 new String[] {table}) > 0;
557     }
558 
559     @VisibleForTesting
560     @Nullable // We return null during tests when migration is not needed or database
561               // is unavailable.
getContactsWritableDatabaseForMigration()562     SQLiteDatabase getContactsWritableDatabaseForMigration() {
563         try {
564             return ContactsDatabaseHelper.getInstance(mContext).getWritableDatabase();
565         } catch (SQLiteCantOpenDatabaseException e) {
566             Log.i(TAG, "Exception caught during opening database for migration: " + e);
567             return null;
568         }
569     }
570 
getPhoneAccountHandleMigrationUtils()571     public PhoneAccountHandleMigrationUtils getPhoneAccountHandleMigrationUtils() {
572         return mPhoneAccountHandleMigrationUtils;
573     }
574 
selectDistinctColumn(String table, String column)575     public ArraySet<String> selectDistinctColumn(String table, String column) {
576         final ArraySet<String> ret = new ArraySet<>();
577         final SQLiteDatabase db = getReadableDatabase();
578         final Cursor c = db.rawQuery("SELECT DISTINCT "
579                 + column
580                 + " FROM " + table, null);
581         try {
582             c.moveToPosition(-1);
583             while (c.moveToNext()) {
584                 if (c.isNull(0)) {
585                     continue;
586                 }
587                 final String s = c.getString(0);
588 
589                 if (!TextUtils.isEmpty(s)) {
590                     ret.add(s);
591                 }
592             }
593             return ret;
594         } finally {
595             c.close();
596         }
597     }
598 
599     @VisibleForTesting
closeForTest()600     void closeForTest() {
601         mOpenHelper.close();
602     }
603 
wipeForTest()604     public void wipeForTest() {
605         getWritableDatabase().execSQL("DELETE FROM " + Tables.CALLS);
606     }
607 
608     @VisibleForTesting
getOpenHelper()609     OpenHelper getOpenHelper() {
610         return mOpenHelper;
611     }
612 }
613