1dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki/*
2dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki * Copyright (C) 2015 The Android Open Source Project
3dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki *
4dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki * Licensed under the Apache License, Version 2.0 (the "License");
5dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki * you may not use this file except in compliance with the License.
6dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki * You may obtain a copy of the License at
7dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki *
8dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki *      http://www.apache.org/licenses/LICENSE-2.0
9dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki *
10dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki * Unless required by applicable law or agreed to in writing, software
11dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki * distributed under the License is distributed on an "AS IS" BASIS,
12dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki * See the License for the specific language governing permissions and
14dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki * limitations under the License
15dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki */
16dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukipackage com.android.providers.contacts;
17dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
18dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport android.annotation.Nullable;
19dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport android.content.ContentValues;
20dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport android.content.Context;
21dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport android.database.Cursor;
22dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport android.database.DatabaseUtils;
23dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport android.database.sqlite.SQLiteDatabase;
24dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport android.database.sqlite.SQLiteOpenHelper;
25dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport android.provider.CallLog.Calls;
26dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport android.provider.VoicemailContract;
2721c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yenimport android.provider.VoicemailContract.Status;
28dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport android.provider.VoicemailContract.Voicemails;
29dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport android.util.Log;
30dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
31dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport com.android.internal.annotations.VisibleForTesting;
32dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukiimport com.android.providers.contacts.util.PropertyUtils;
33dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
34dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki/**
35dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki * SQLite database (helper) for {@link CallLogProvider} and {@link VoicemailContentProvider}.
36dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki */
37dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onukipublic class CallLogDatabaseHelper {
38dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    private static final String TAG = "CallLogDatabaseHelper";
39dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
4021c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen    private static final int DATABASE_VERSION = 3;
41dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
42dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    private static final boolean DEBUG = false; // DON'T SUBMIT WITH TRUE
43dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
44dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    private static final String DATABASE_NAME = "calllog.db";
45dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
46f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki    private static final String SHADOW_DATABASE_NAME = "calllog_shadow.db";
47f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki
48dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    private static CallLogDatabaseHelper sInstance;
49dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
50f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki    /** Instance for the "shadow" provider. */
51f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki    private static CallLogDatabaseHelper sInstanceForShadow;
52f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki
53dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    private final Context mContext;
54dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
55dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    private final OpenHelper mOpenHelper;
56dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
57dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    public interface Tables {
58dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        String CALLS = "calls";
59dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        String VOICEMAIL_STATUS = "voicemail_status";
60dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
61dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
62dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    public interface DbProperties {
63dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        String CALL_LOG_LAST_SYNCED = "call_log_last_synced";
64f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki        String CALL_LOG_LAST_SYNCED_FOR_SHADOW = "call_log_last_synced_for_shadow";
65dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        String DATA_MIGRATED = "migrated";
66dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
67dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
68dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    /**
69dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki     * Constants used in the contacts DB helper, which are needed for migration.
70dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki     *
71dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki     * DO NOT CHANCE ANY OF THE CONSTANTS.
72dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki     */
73dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    private interface LegacyConstants {
74dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        /** Table name used in the contacts DB.*/
75dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        String CALLS_LEGACY = "calls";
76dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
77dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        /** Table name used in the contacts DB.*/
78dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        String VOICEMAIL_STATUS_LEGACY = "voicemail_status";
79dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
80dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        /** Prop name used in the contacts DB.*/
81dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        String CALL_LOG_LAST_SYNCED_LEGACY = "call_log_last_synced";
82dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
83dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
84dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    private final class OpenHelper extends SQLiteOpenHelper {
85dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        public OpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,
86dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                int version) {
87dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            super(context, name, factory, version);
88dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        }
89dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
90dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        @Override
91dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        public void onCreate(SQLiteDatabase db) {
92dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            if (DEBUG) {
93dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                Log.d(TAG, "onCreate");
94dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            }
95dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
96dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            PropertyUtils.createPropertiesTable(db);
97dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
98dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // *** NOTE ABOUT CHANGING THE DB SCHEMA ***
99dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            //
100dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // The CALLS and VOICEMAIL_STATUS table used to be in the contacts2.db.  So we need to
101dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // migrate from these legacy tables, if exist, after creating the calllog DB, which is
102dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // done in migrateFromLegacyTables().
103dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            //
104dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // This migration is slightly different from a regular upgrade step, because it's always
105dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // performed from the legacy schema (of the latest version -- because the migration
106dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // source is always the latest DB after all the upgrade steps) to the *latest* schema
107dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // at once.
108dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            //
109dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // This means certain kind of changes are not doable without changing the
110dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // migration logic.  For example, if you rename a column in the DB, the migration step
111dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // will need to be updated to handle the column name change.
112dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
113dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            db.execSQL("CREATE TABLE " + Tables.CALLS + " (" +
114dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
115dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.NUMBER + " TEXT," +
116dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.NUMBER_PRESENTATION + " INTEGER NOT NULL DEFAULT " +
117dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.PRESENTATION_ALLOWED + "," +
118dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.POST_DIAL_DIGITS + " TEXT NOT NULL DEFAULT ''," +
119c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger                    Calls.VIA_NUMBER + " TEXT NOT NULL DEFAULT ''," +
120dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.DATE + " INTEGER," +
121dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.DURATION + " INTEGER," +
122dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.DATA_USAGE + " INTEGER," +
123dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.TYPE + " INTEGER," +
124dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.FEATURES + " INTEGER NOT NULL DEFAULT 0," +
125dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.PHONE_ACCOUNT_COMPONENT_NAME + " TEXT," +
126dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.PHONE_ACCOUNT_ID + " TEXT," +
127dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.PHONE_ACCOUNT_ADDRESS + " TEXT," +
128dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.PHONE_ACCOUNT_HIDDEN + " INTEGER NOT NULL DEFAULT 0," +
129dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.SUB_ID + " INTEGER DEFAULT -1," +
130dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.NEW + " INTEGER," +
131dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.CACHED_NAME + " TEXT," +
132dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.CACHED_NUMBER_TYPE + " INTEGER," +
133dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.CACHED_NUMBER_LABEL + " TEXT," +
134dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.COUNTRY_ISO + " TEXT," +
135dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.VOICEMAIL_URI + " TEXT," +
136dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.IS_READ + " INTEGER," +
137dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.GEOCODED_LOCATION + " TEXT," +
138dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.CACHED_LOOKUP_URI + " TEXT," +
139dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.CACHED_MATCHED_NUMBER + " TEXT," +
140dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.CACHED_NORMALIZED_NUMBER + " TEXT," +
141dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.CACHED_PHOTO_ID + " INTEGER NOT NULL DEFAULT 0," +
142dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.CACHED_PHOTO_URI + " TEXT," +
143dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.CACHED_FORMATTED_NUMBER + " TEXT," +
144dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.ADD_FOR_ALL_USERS + " INTEGER NOT NULL DEFAULT 1," +
145dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Calls.LAST_MODIFIED + " INTEGER DEFAULT 0," +
146dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Voicemails._DATA + " TEXT," +
147dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Voicemails.HAS_CONTENT + " INTEGER," +
148dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Voicemails.MIME_TYPE + " TEXT," +
149dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Voicemails.SOURCE_DATA + " TEXT," +
150dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Voicemails.SOURCE_PACKAGE + " TEXT," +
151dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Voicemails.TRANSCRIPTION + " TEXT," +
152dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Voicemails.STATE + " INTEGER," +
153dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Voicemails.DIRTY + " INTEGER NOT NULL DEFAULT 0," +
154dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    Voicemails.DELETED + " INTEGER NOT NULL DEFAULT 0" +
155dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    ");");
156dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
157dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            db.execSQL("CREATE TABLE " + Tables.VOICEMAIL_STATUS + " (" +
158dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    VoicemailContract.Status._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
159dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    VoicemailContract.Status.SOURCE_PACKAGE + " TEXT NOT NULL," +
160dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    VoicemailContract.Status.PHONE_ACCOUNT_COMPONENT_NAME + " TEXT," +
161dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    VoicemailContract.Status.PHONE_ACCOUNT_ID + " TEXT," +
162dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    VoicemailContract.Status.SETTINGS_URI + " TEXT," +
163dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    VoicemailContract.Status.VOICEMAIL_ACCESS_URI + " TEXT," +
164dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    VoicemailContract.Status.CONFIGURATION_STATE + " INTEGER," +
165dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    VoicemailContract.Status.DATA_CHANNEL_STATE + " INTEGER," +
166dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE + " INTEGER," +
167dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    VoicemailContract.Status.QUOTA_OCCUPIED + " INTEGER DEFAULT -1," +
16821c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen                    VoicemailContract.Status.QUOTA_TOTAL + " INTEGER DEFAULT -1," +
16921c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen                    VoicemailContract.Status.SOURCE_TYPE + " TEXT" +
170dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    ");");
171dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
172dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            migrateFromLegacyTables(db);
173dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        }
174dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
175dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        @Override
176dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
177dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            if (DEBUG) {
178dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                Log.d(TAG, "onUpgrade");
179dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            }
180dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
181c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger            if (oldVersion < 2) {
182c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger                upgradeToVersion2(db);
183c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger            }
18421c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen
18521c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen            if (oldVersion < 3) {
18621c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen                upgradeToVersion3(db);
18721c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen            }
188dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        }
189dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
190dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
191dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    @VisibleForTesting
192dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    CallLogDatabaseHelper(Context context, String databaseName) {
193dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        mContext = context;
194dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        mOpenHelper = new OpenHelper(mContext, databaseName, /* factory=*/ null, DATABASE_VERSION);
195dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
196dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
197dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    public static synchronized CallLogDatabaseHelper getInstance(Context context) {
198dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        if (sInstance == null) {
199dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            sInstance = new CallLogDatabaseHelper(context, DATABASE_NAME);
200dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        }
201dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        return sInstance;
202dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
203dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
204f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki    public static synchronized CallLogDatabaseHelper getInstanceForShadow(Context context) {
205f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki        if (sInstanceForShadow == null) {
206f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki            // Shadow provider is always encryption-aware.
207f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki            sInstanceForShadow = new CallLogDatabaseHelper(
2086364dc89b6c90dab11997bd360a1e80d9bf282caJeff Sharkey                    context.createDeviceProtectedStorageContext(), SHADOW_DATABASE_NAME);
209f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki        }
210f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki        return sInstanceForShadow;
211f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki    }
212f3bd980c0ec03db2538e86cadee2c1559f02c85bMakoto Onuki
213dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    public SQLiteDatabase getReadableDatabase() {
214dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        return mOpenHelper.getReadableDatabase();
215dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
216dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
217dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    public SQLiteDatabase getWritableDatabase() {
218dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        return mOpenHelper.getWritableDatabase();
219dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
220dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
221dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    public String getProperty(String key, String defaultValue) {
222dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        return PropertyUtils.getProperty(getReadableDatabase(), key, defaultValue);
223dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
224dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
225dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    public void setProperty(String key, String value) {
226dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        PropertyUtils.setProperty(getWritableDatabase(), key, value);
227dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
228dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
229dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    /**
230c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger     * Add the {@link Calls.VIA_NUMBER} Column to the CallLog Database.
231c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger     */
232c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger    private void upgradeToVersion2(SQLiteDatabase db) {
233c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger        db.execSQL("ALTER TABLE " + Tables.CALLS + " ADD " + Calls.VIA_NUMBER +
234c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger                " TEXT NOT NULL DEFAULT ''");
235c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger    }
236c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger
237c8ad9ce59d7ca2c10f4652ee3a0ef4e5d003f767Brad Ebinger    /**
23821c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen     * Add the {@link Status.SOURCE_TYPE} Column to the VoicemailStatus Database.
23921c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen     */
24021c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen    private void upgradeToVersion3(SQLiteDatabase db) {
24121c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen        db.execSQL("ALTER TABLE " + Tables.VOICEMAIL_STATUS + " ADD " + Status.SOURCE_TYPE +
24221c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen                " TEXT");
24321c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen    }
24421c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen
24521c692d49ce7ad6f9414d46ac2410d3f2e4c4d20Ta-wei Yen    /**
246dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki     * Perform the migration from the contacts2.db (of the latest version) to the current calllog/
247dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki     * voicemail status tables.
248dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki     */
249dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    private void migrateFromLegacyTables(SQLiteDatabase calllog) {
250dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        final SQLiteDatabase contacts = getContactsWritableDatabaseForMigration();
251dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
252dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        if (contacts == null) {
253dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            Log.w(TAG, "Contacts DB == null, skipping migration. (running tests?)");
254dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            return;
255dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        }
256dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        if (DEBUG) {
257dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            Log.d(TAG, "migrateFromLegacyTables");
258dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        }
259dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
260dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        if ("1".equals(PropertyUtils.getProperty(calllog, DbProperties.DATA_MIGRATED, ""))) {
261dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            return;
262dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        }
263dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
264dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        Log.i(TAG, "Migrating from old tables...");
265dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
266dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        contacts.beginTransaction();
267dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        try {
268dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            if (!tableExists(contacts, LegacyConstants.CALLS_LEGACY)
269dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    || !tableExists(contacts, LegacyConstants.VOICEMAIL_STATUS_LEGACY)) {
270dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                // This is fine on new devices. (or after a "clear data".)
271dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                Log.i(TAG, "Source tables don't exist.");
272dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                return;
273dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            }
274dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            calllog.beginTransaction();
275dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            try {
276dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
277dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                final ContentValues cv = new ContentValues();
278dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
279dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                try (Cursor source = contacts.rawQuery(
280dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                        "SELECT * FROM " + LegacyConstants.CALLS_LEGACY, null)) {
281dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    while (source.moveToNext()) {
282dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                        cv.clear();
283dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
284dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                        DatabaseUtils.cursorRowToContentValues(source, cv);
285dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
286dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                        calllog.insertOrThrow(Tables.CALLS, null, cv);
287dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    }
288dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                }
289dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
290dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                try (Cursor source = contacts.rawQuery("SELECT * FROM " +
291dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                        LegacyConstants.VOICEMAIL_STATUS_LEGACY, null)) {
292dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    while (source.moveToNext()) {
293dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                        cv.clear();
294dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
295dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                        DatabaseUtils.cursorRowToContentValues(source, cv);
296dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
297dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                        calllog.insertOrThrow(Tables.VOICEMAIL_STATUS, null, cv);
298dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                    }
299dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                }
300dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
301dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                contacts.execSQL("DROP TABLE " + LegacyConstants.CALLS_LEGACY + ";");
302dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                contacts.execSQL("DROP TABLE " + LegacyConstants.VOICEMAIL_STATUS_LEGACY + ";");
303dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
304dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                // Also copy the last sync time.
305dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                PropertyUtils.setProperty(calllog, DbProperties.CALL_LOG_LAST_SYNCED,
306dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                        PropertyUtils.getProperty(contacts,
307dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                                LegacyConstants.CALL_LOG_LAST_SYNCED_LEGACY, null));
308dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
309dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                Log.i(TAG, "Migration completed.");
310dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
311dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                calllog.setTransactionSuccessful();
312dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            } finally {
313dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                calllog.endTransaction();
314dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            }
315dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
316dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            contacts.setTransactionSuccessful();
317dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        } catch (RuntimeException e) {
318dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            // We don't want to be stuck here, so we just swallow exceptions...
319dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            Log.w(TAG, "Exception caught during migration", e);
320dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        } finally {
321dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki            contacts.endTransaction();
322dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        }
323dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        PropertyUtils.setProperty(calllog, DbProperties.DATA_MIGRATED, "1");
324dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
325dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
326dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    @VisibleForTesting
327dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    static boolean tableExists(SQLiteDatabase db, String table) {
328dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        return DatabaseUtils.longForQuery(db,
329dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                "select count(*) from sqlite_master where type='table' and name=?",
330dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki                new String[] {table}) > 0;
331dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
332dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
333dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    @VisibleForTesting
334dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    @Nullable // We return null during tests when migration is not needed.
335dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    SQLiteDatabase getContactsWritableDatabaseForMigration() {
336dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        return ContactsDatabaseHelper.getInstance(mContext).getWritableDatabase();
337dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
338dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
339dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    @VisibleForTesting
340dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    void closeForTest() {
341dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        mOpenHelper.close();
342dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
343dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki
344dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    public void wipeForTest() {
345dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki        getWritableDatabase().execSQL("DELETE FROM " + Tables.CALLS);
346dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki    }
347dc653a5c1bed274512ce41e4a6129a65d2b0eeacMakoto Onuki}
348