blob: ceeecbc311d8db704bde5f66fe32d4063ac017b5 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.database.sqlite;
18
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -070019import android.annotation.IntRange;
Fyodor Kupolov13a4b372017-11-07 18:45:35 -080020import android.annotation.NonNull;
21import android.annotation.Nullable;
Mathew Inwood41b31942018-08-10 16:00:53 +010022import android.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.Context;
Vasu Nori74f170f2010-06-01 18:06:18 -070024import android.database.DatabaseErrorHandler;
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -070025import android.database.SQLException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.database.sqlite.SQLiteDatabase.CursorFactory;
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -070027import android.os.FileUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.util.Log;
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -070029
Fyodor Kupolov13a4b372017-11-07 18:45:35 -080030import com.android.internal.util.Preconditions;
31
Suprabh Shukla6222176962016-10-06 14:41:35 -070032import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033
34/**
35 * A helper class to manage database creation and version management.
Dan Egnor6fcc0f0732010-07-27 16:32:17 -070036 *
37 * <p>You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038 * optionally {@link #onOpen}, and this class takes care of opening the database
39 * if it exists, creating it if it does not, and upgrading it as necessary.
40 * Transactions are used to make sure the database is always in a sensible state.
Dan Egnor6fcc0f0732010-07-27 16:32:17 -070041 *
42 * <p>This class makes it easy for {@link android.content.ContentProvider}
43 * implementations to defer opening and upgrading the database until first use,
44 * to avoid blocking application startup with long-running database upgrades.
45 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046 * <p>For an example, see the NotePadProvider class in the NotePad sample application,
47 * in the <em>samples/</em> directory of the SDK.</p>
Brad Fitzpatrick44dc76a2010-06-02 15:12:05 -070048 *
49 * <p class="note"><strong>Note:</strong> this class assumes
Eric Hassolda5af5d62010-12-13 12:51:02 -080050 * monotonically increasing version numbers for upgrades.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 */
Jeff Sharkeyec5f7d12018-08-08 09:15:04 -060052public abstract class SQLiteOpenHelper implements AutoCloseable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
54
55 private final Context mContext;
Mathew Inwood41b31942018-08-10 16:00:53 +010056 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057 private final String mName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 private final int mNewVersion;
Suprabh Shukla6222176962016-10-06 14:41:35 -070059 private final int mMinimumSupportedVersion;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060
Jeff Brown559d0642012-02-29 10:19:12 -080061 private SQLiteDatabase mDatabase;
62 private boolean mIsInitializing;
Fyodor Kupolovab05b142018-02-12 15:33:13 -080063 private SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
65 /**
66 * Create a helper object to create, open, and/or manage a database.
Dan Egnor6fcc0f0732010-07-27 16:32:17 -070067 * This method always returns very quickly. The database is not actually
68 * created or opened until one of {@link #getWritableDatabase} or
69 * {@link #getReadableDatabase} is called.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 *
Fyodor Kupolov2abd2a42018-01-17 16:45:14 -080071 * @param context to use for locating paths to the the database
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 * @param name of the database file, or null for an in-memory database
73 * @param factory to use for creating cursor objects, or null for the default
74 * @param version number of the database (starting at 1); if the database is older,
Eric Hassolda5af5d62010-12-13 12:51:02 -080075 * {@link #onUpgrade} will be used to upgrade the database; if the database is
76 * newer, {@link #onDowngrade} will be used to downgrade the database
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 */
Fyodor Kupolov9d4c3d92017-11-15 16:04:24 -080078 public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
Fyodor Kupolov13a4b372017-11-07 18:45:35 -080079 @Nullable CursorFactory factory, int version) {
Jeff Brown47847f32012-03-22 19:13:11 -070080 this(context, name, factory, version, null);
Vasu Nori74f170f2010-06-01 18:06:18 -070081 }
82
83 /**
84 * Create a helper object to create, open, and/or manage a database.
85 * The database is not actually created or opened until one of
86 * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
87 *
88 * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
89 * used to handle corruption when sqlite reports database corruption.</p>
90 *
Fyodor Kupolov2abd2a42018-01-17 16:45:14 -080091 * @param context to use for locating paths to the the database
Vasu Nori74f170f2010-06-01 18:06:18 -070092 * @param name of the database file, or null for an in-memory database
93 * @param factory to use for creating cursor objects, or null for the default
94 * @param version number of the database (starting at 1); if the database is older,
Pin Ting1c423b82012-01-18 11:21:06 +080095 * {@link #onUpgrade} will be used to upgrade the database; if the database is
96 * newer, {@link #onDowngrade} will be used to downgrade the database
Vasu Nori74f170f2010-06-01 18:06:18 -070097 * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
Jeff Brown47847f32012-03-22 19:13:11 -070098 * corruption, or null to use the default error handler.
Vasu Nori74f170f2010-06-01 18:06:18 -070099 */
Fyodor Kupolov9d4c3d92017-11-15 16:04:24 -0800100 public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800101 @Nullable CursorFactory factory, int version,
102 @Nullable DatabaseErrorHandler errorHandler) {
Suprabh Shukla6222176962016-10-06 14:41:35 -0700103 this(context, name, factory, version, 0, errorHandler);
104 }
105
106 /**
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800107 * Create a helper object to create, open, and/or manage a database.
108 * This method always returns very quickly. The database is not actually
109 * created or opened until one of {@link #getWritableDatabase} or
110 * {@link #getReadableDatabase} is called.
111 *
Fyodor Kupolov2abd2a42018-01-17 16:45:14 -0800112 * @param context to use for locating paths to the the database
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800113 * @param name of the database file, or null for an in-memory database
114 * @param version number of the database (starting at 1); if the database is older,
115 * {@link #onUpgrade} will be used to upgrade the database; if the database is
116 * newer, {@link #onDowngrade} will be used to downgrade the database
117 * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
118 * Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be
119 * set when the helper opens the database
120 */
Fyodor Kupolov9d4c3d92017-11-15 16:04:24 -0800121 public SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800122 @NonNull SQLiteDatabase.OpenParams openParams) {
123 this(context, name, version, 0, openParams.toBuilder());
124 }
125
126 /**
Suprabh Shukla6222176962016-10-06 14:41:35 -0700127 * Same as {@link #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler)}
128 * but also accepts an integer minimumSupportedVersion as a convenience for upgrading very old
129 * versions of this database that are no longer supported. If a database with older version that
130 * minimumSupportedVersion is found, it is simply deleted and a new database is created with the
131 * given name and version
132 *
Fyodor Kupolov2abd2a42018-01-17 16:45:14 -0800133 * @param context to use for locating paths to the the database
Suprabh Shukla6222176962016-10-06 14:41:35 -0700134 * @param name the name of the database file, null for a temporary in-memory database
135 * @param factory to use for creating cursor objects, null for default
136 * @param version the required version of the database
137 * @param minimumSupportedVersion the minimum version that is supported to be upgraded to
138 * {@code version} via {@link #onUpgrade}. If the current database version is lower
139 * than this, database is simply deleted and recreated with the version passed in
140 * {@code version}. {@link #onBeforeDelete} is called before deleting the database
141 * when this happens. This is 0 by default.
142 * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
143 * corruption, or null to use the default error handler.
144 * @see #onBeforeDelete(SQLiteDatabase)
145 * @see #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler)
146 * @see #onUpgrade(SQLiteDatabase, int, int)
147 * @hide
148 */
Fyodor Kupolov9d4c3d92017-11-15 16:04:24 -0800149 public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800150 @Nullable CursorFactory factory, int version,
151 int minimumSupportedVersion, @Nullable DatabaseErrorHandler errorHandler) {
152 this(context, name, version, minimumSupportedVersion,
153 new SQLiteDatabase.OpenParams.Builder());
154 mOpenParamsBuilder.setCursorFactory(factory);
155 mOpenParamsBuilder.setErrorHandler(errorHandler);
156 }
157
Fyodor Kupolov9d4c3d92017-11-15 16:04:24 -0800158 private SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800159 int minimumSupportedVersion,
160 @NonNull SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800161 Preconditions.checkNotNull(openParamsBuilder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
163
164 mContext = context;
165 mName = name;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 mNewVersion = version;
Suprabh Shukla6222176962016-10-06 14:41:35 -0700167 mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
Fyodor Kupolovab05b142018-02-12 15:33:13 -0800168 setOpenParamsBuilder(openParamsBuilder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 }
170
171 /**
Pin Ting1c423b82012-01-18 11:21:06 +0800172 * Return the name of the SQLite database being opened, as given to
Dianne Hackborn661cd522011-08-22 00:26:20 -0700173 * the constructor.
174 */
175 public String getDatabaseName() {
176 return mName;
177 }
178
179 /**
Jeff Brown47847f32012-03-22 19:13:11 -0700180 * Enables or disables the use of write-ahead logging for the database.
181 *
182 * Write-ahead logging cannot be used with read-only databases so the value of
183 * this flag is ignored if the database is opened read-only.
184 *
185 * @param enabled True if write-ahead logging should be enabled, false if it
186 * should be disabled.
187 *
188 * @see SQLiteDatabase#enableWriteAheadLogging()
189 */
190 public void setWriteAheadLoggingEnabled(boolean enabled) {
191 synchronized (this) {
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700192 if (mOpenParamsBuilder.isWriteAheadLoggingEnabled() != enabled) {
Jeff Brown47847f32012-03-22 19:13:11 -0700193 if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
194 if (enabled) {
195 mDatabase.enableWriteAheadLogging();
196 } else {
197 mDatabase.disableWriteAheadLogging();
198 }
199 }
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700200 mOpenParamsBuilder.setWriteAheadLoggingEnabled(enabled);
Jeff Brown47847f32012-03-22 19:13:11 -0700201 }
Fyodor Kupolov692573b2018-03-06 12:34:36 -0800202 // Compatibility WAL is disabled if an app disables or enables WAL
203 mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.DISABLE_COMPATIBILITY_WAL);
Jeff Brown47847f32012-03-22 19:13:11 -0700204 }
205 }
206
207 /**
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700208 * Configures <a href="https://sqlite.org/malloc.html#lookaside">lookaside memory allocator</a>
209 *
210 * <p>This method should be called from the constructor of the subclass,
211 * before opening the database, since lookaside memory configuration can only be changed
212 * when no connection is using it
213 *
214 * <p>SQLite default settings will be used, if this method isn't called.
215 * Use {@code setLookasideConfig(0,0)} to disable lookaside
216 *
Fyodor Kupolov05a0f0f2017-06-30 19:00:00 -0700217 * <p><strong>Note:</strong> Provided slotSize/slotCount configuration is just a recommendation.
218 * The system may choose different values depending on a device, e.g. lookaside allocations
219 * can be disabled on low-RAM devices
220 *
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700221 * @param slotSize The size in bytes of each lookaside slot.
222 * @param slotCount The total number of lookaside memory slots per database connection.
223 */
224 public void setLookasideConfig(@IntRange(from = 0) final int slotSize,
225 @IntRange(from = 0) final int slotCount) {
226 synchronized (this) {
227 if (mDatabase != null && mDatabase.isOpen()) {
228 throw new IllegalStateException(
229 "Lookaside memory config cannot be changed after opening the database");
230 }
231 mOpenParamsBuilder.setLookasideConfig(slotSize, slotCount);
232 }
233 }
234
235 /**
Fyodor Kupolovab05b142018-02-12 15:33:13 -0800236 * Sets configuration parameters that are used for opening {@link SQLiteDatabase}.
237 * <p>Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be set when
238 * opening the database
239 *
240 * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
241 * @throws IllegalStateException if the database is already open
242 */
243 public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) {
244 Preconditions.checkNotNull(openParams);
245 synchronized (this) {
246 if (mDatabase != null && mDatabase.isOpen()) {
247 throw new IllegalStateException(
248 "OpenParams cannot be set after opening the database");
249 }
250 setOpenParamsBuilder(new SQLiteDatabase.OpenParams.Builder(openParams));
251 }
252 }
253
254 private void setOpenParamsBuilder(SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
255 mOpenParamsBuilder = openParamsBuilder;
256 mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
257 }
258
259 /**
Fyodor Kupolovcf97b6b2017-07-25 14:17:33 -0700260 * Sets the maximum number of milliseconds that SQLite connection is allowed to be idle
261 * before it is closed and removed from the pool.
262 *
263 * <p>This method should be called from the constructor of the subclass,
264 * before opening the database
265 *
Makoto Onukib15186c2019-01-28 14:43:41 -0800266 * <p>DO NOT USE this method unless you fully understand the implication
267 * of what it does.
268 * See the javadoc of
269 * {@link SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)}
270 * for the details.
271 *
Fyodor Kupolovcf97b6b2017-07-25 14:17:33 -0700272 * @param idleConnectionTimeoutMs timeout in milliseconds. Use {@link Long#MAX_VALUE} value
273 * to allow unlimited idle connections.
Makoto Onukib15186c2019-01-28 14:43:41 -0800274 *
275 * @see SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)
276 *
277 * @deprecated DO NOT USE this method unless you fully understand the implication
278 * of what it does.
Fyodor Kupolovcf97b6b2017-07-25 14:17:33 -0700279 */
Makoto Onukib15186c2019-01-28 14:43:41 -0800280 @Deprecated
Fyodor Kupolovcf97b6b2017-07-25 14:17:33 -0700281 public void setIdleConnectionTimeout(@IntRange(from = 0) final long idleConnectionTimeoutMs) {
282 synchronized (this) {
283 if (mDatabase != null && mDatabase.isOpen()) {
284 throw new IllegalStateException(
285 "Connection timeout setting cannot be changed after opening the database");
286 }
287 mOpenParamsBuilder.setIdleConnectionTimeout(idleConnectionTimeoutMs);
288 }
289 }
290
291 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 * Create and/or open a database that will be used for reading and writing.
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700293 * The first time this is called, the database will be opened and
294 * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
295 * called.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 *
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700297 * <p>Once opened successfully, the database is cached, so you can
298 * call this method every time you need to write to the database.
299 * (Make sure to call {@link #close} when you no longer need the database.)
300 * Errors such as bad permissions or a full disk may cause this method
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 * to fail, but future attempts may succeed if the problem is fixed.</p>
302 *
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700303 * <p class="caution">Database upgrade may take a long time, you
304 * should not call this method from the application main thread, including
305 * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
306 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 * @throws SQLiteException if the database cannot be opened for writing
308 * @return a read/write database object valid until {@link #close} is called
309 */
Jeff Brown559d0642012-02-29 10:19:12 -0800310 public SQLiteDatabase getWritableDatabase() {
311 synchronized (this) {
312 return getDatabaseLocked(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 }
314 }
315
316 /**
317 * Create and/or open a database. This will be the same object returned by
318 * {@link #getWritableDatabase} unless some problem, such as a full disk,
319 * requires the database to be opened read-only. In that case, a read-only
320 * database object will be returned. If the problem is fixed, a future call
321 * to {@link #getWritableDatabase} may succeed, in which case the read-only
322 * database object will be closed and the read/write object will be returned
323 * in the future.
324 *
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700325 * <p class="caution">Like {@link #getWritableDatabase}, this method may
326 * take a long time to return, so you should not call it from the
327 * application main thread, including from
328 * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
329 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 * @throws SQLiteException if the database cannot be opened
331 * @return a database object valid until {@link #getWritableDatabase}
332 * or {@link #close} is called.
333 */
Jeff Brown559d0642012-02-29 10:19:12 -0800334 public SQLiteDatabase getReadableDatabase() {
335 synchronized (this) {
336 return getDatabaseLocked(false);
337 }
338 }
339
340 private SQLiteDatabase getDatabaseLocked(boolean writable) {
Vasu Noricc6f5492010-08-23 17:05:25 -0700341 if (mDatabase != null) {
342 if (!mDatabase.isOpen()) {
Jeff Brown559d0642012-02-29 10:19:12 -0800343 // Darn! The user closed the database by calling mDatabase.close().
Vasu Noricc6f5492010-08-23 17:05:25 -0700344 mDatabase = null;
Jeff Brown559d0642012-02-29 10:19:12 -0800345 } else if (!writable || !mDatabase.isReadOnly()) {
346 // The database is already open for business.
347 return mDatabase;
Vasu Noricc6f5492010-08-23 17:05:25 -0700348 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 }
350
351 if (mIsInitializing) {
Jeff Brown559d0642012-02-29 10:19:12 -0800352 throw new IllegalStateException("getDatabase called recursively");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 }
354
Jeff Brown559d0642012-02-29 10:19:12 -0800355 SQLiteDatabase db = mDatabase;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 try {
357 mIsInitializing = true;
Jeff Brown559d0642012-02-29 10:19:12 -0800358
359 if (db != null) {
360 if (writable && db.isReadOnly()) {
361 db.reopenReadWrite();
362 }
363 } else if (mName == null) {
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700364 db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
Jeff Brown559d0642012-02-29 10:19:12 -0800365 } else {
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700366 final File filePath = mContext.getDatabasePath(mName);
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700367 SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
Jeff Brown559d0642012-02-29 10:19:12 -0800368 try {
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700369 db = SQLiteDatabase.openDatabase(filePath, params);
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700370 // Keep pre-O-MR1 behavior by resetting file permissions to 660
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700371 setFilePermissionsForDb(filePath.getPath());
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700372 } catch (SQLException ex) {
Jeff Brown559d0642012-02-29 10:19:12 -0800373 if (writable) {
374 throw ex;
375 }
376 Log.e(TAG, "Couldn't open " + mName
377 + " for writing (will try read-only):", ex);
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700378 params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700379 db = SQLiteDatabase.openDatabase(filePath, params);
Jeff Brown559d0642012-02-29 10:19:12 -0800380 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 }
382
Jeff Brown96496ad2012-03-23 14:38:06 -0700383 onConfigure(db);
384
Jeff Brown559d0642012-02-29 10:19:12 -0800385 final int version = db.getVersion();
386 if (version != mNewVersion) {
387 if (db.isReadOnly()) {
388 throw new SQLiteException("Can't upgrade read-only database from version " +
389 db.getVersion() + " to " + mNewVersion + ": " + mName);
390 }
391
Suprabh Shukla6222176962016-10-06 14:41:35 -0700392 if (version > 0 && version < mMinimumSupportedVersion) {
393 File databaseFile = new File(db.getPath());
394 onBeforeDelete(db);
395 db.close();
396 if (SQLiteDatabase.deleteDatabase(databaseFile)) {
397 mIsInitializing = false;
398 return getDatabaseLocked(writable);
Jeff Brown559d0642012-02-29 10:19:12 -0800399 } else {
Suprabh Shukla6222176962016-10-06 14:41:35 -0700400 throw new IllegalStateException("Unable to delete obsolete database "
401 + mName + " with version " + version);
Jeff Brown559d0642012-02-29 10:19:12 -0800402 }
Suprabh Shukla6222176962016-10-06 14:41:35 -0700403 } else {
404 db.beginTransaction();
405 try {
406 if (version == 0) {
407 onCreate(db);
408 } else {
409 if (version > mNewVersion) {
410 onDowngrade(db, version, mNewVersion);
411 } else {
412 onUpgrade(db, version, mNewVersion);
413 }
414 }
415 db.setVersion(mNewVersion);
416 db.setTransactionSuccessful();
417 } finally {
418 db.endTransaction();
419 }
Jeff Brown559d0642012-02-29 10:19:12 -0800420 }
421 }
Jeff Brown96496ad2012-03-23 14:38:06 -0700422
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 onOpen(db);
Jeff Brown559d0642012-02-29 10:19:12 -0800424
425 if (db.isReadOnly()) {
426 Log.w(TAG, "Opened " + mName + " in read-only mode");
427 }
428
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 mDatabase = db;
Jeff Brown559d0642012-02-29 10:19:12 -0800430 return db;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 } finally {
432 mIsInitializing = false;
Jeff Brown559d0642012-02-29 10:19:12 -0800433 if (db != null && db != mDatabase) {
434 db.close();
435 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 }
437 }
438
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700439 private static void setFilePermissionsForDb(String dbPath) {
440 int perms = FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IWGRP;
441 FileUtils.setPermissions(dbPath, perms, -1, -1);
442 }
443
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 /**
445 * Close any open database object.
446 */
447 public synchronized void close() {
448 if (mIsInitializing) throw new IllegalStateException("Closed during initialization");
449
450 if (mDatabase != null && mDatabase.isOpen()) {
451 mDatabase.close();
452 mDatabase = null;
453 }
454 }
455
456 /**
Suprabh Shukla6222176962016-10-06 14:41:35 -0700457 * Called when the database connection is being configured, to enable features such as
458 * write-ahead logging or foreign key support.
Jeff Brown96496ad2012-03-23 14:38:06 -0700459 * <p>
Suprabh Shukla6222176962016-10-06 14:41:35 -0700460 * This method is called before {@link #onCreate}, {@link #onUpgrade}, {@link #onDowngrade}, or
461 * {@link #onOpen} are called. It should not modify the database except to configure the
462 * database connection as required.
463 * </p>
464 * <p>
465 * This method should only call methods that configure the parameters of the database
466 * connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
467 * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled}, {@link SQLiteDatabase#setLocale},
468 * {@link SQLiteDatabase#setMaximumSize}, or executing PRAGMA statements.
Jeff Brown96496ad2012-03-23 14:38:06 -0700469 * </p>
470 *
471 * @param db The database.
472 */
473 public void onConfigure(SQLiteDatabase db) {}
474
475 /**
Suprabh Shukla6222176962016-10-06 14:41:35 -0700476 * Called before the database is deleted when the version returned by
477 * {@link SQLiteDatabase#getVersion()} is lower than the minimum supported version passed (if at
478 * all) while creating this helper. After the database is deleted, a fresh database with the
479 * given version is created. This will be followed by {@link #onConfigure(SQLiteDatabase)} and
480 * {@link #onCreate(SQLiteDatabase)} being called with a new SQLiteDatabase object
481 *
482 * @param db the database opened with this helper
483 * @see #SQLiteOpenHelper(Context, String, CursorFactory, int, int, DatabaseErrorHandler)
484 * @hide
485 */
486 public void onBeforeDelete(SQLiteDatabase db) {
487 }
488
489 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 * Called when the database is created for the first time. This is where the
491 * creation of tables and the initial population of the tables should happen.
492 *
493 * @param db The database.
494 */
495 public abstract void onCreate(SQLiteDatabase db);
496
497 /**
498 * Called when the database needs to be upgraded. The implementation
499 * should use this method to drop tables, add tables, or do anything else it
500 * needs to upgrade to the new schema version.
501 *
Jeff Brown96496ad2012-03-23 14:38:06 -0700502 * <p>
503 * The SQLite ALTER TABLE documentation can be found
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 * <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
505 * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
506 * you can use ALTER TABLE to rename the old table, then create the new table and then
507 * populate the new table with the contents of the old table.
Jeff Brown96496ad2012-03-23 14:38:06 -0700508 * </p><p>
509 * This method executes within a transaction. If an exception is thrown, all changes
510 * will automatically be rolled back.
511 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 *
513 * @param db The database.
514 * @param oldVersion The old database version.
515 * @param newVersion The new database version.
516 */
517 public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
518
519 /**
Pin Ting1c423b82012-01-18 11:21:06 +0800520 * Called when the database needs to be downgraded. This is strictly similar to
Jeff Brown96496ad2012-03-23 14:38:06 -0700521 * {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
Eric Hassolda5af5d62010-12-13 12:51:02 -0800522 * However, this method is not abstract, so it is not mandatory for a customer to
523 * implement it. If not overridden, default implementation will reject downgrade and
524 * throws SQLiteException
525 *
Jeff Brown96496ad2012-03-23 14:38:06 -0700526 * <p>
527 * This method executes within a transaction. If an exception is thrown, all changes
528 * will automatically be rolled back.
529 * </p>
530 *
Eric Hassolda5af5d62010-12-13 12:51:02 -0800531 * @param db The database.
532 * @param oldVersion The old database version.
533 * @param newVersion The new database version.
534 */
535 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
536 throw new SQLiteException("Can't downgrade database from version " +
537 oldVersion + " to " + newVersion);
538 }
539
540 /**
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700541 * Called when the database has been opened. The implementation
542 * should check {@link SQLiteDatabase#isReadOnly} before updating the
543 * database.
Jeff Brown96496ad2012-03-23 14:38:06 -0700544 * <p>
545 * This method is called after the database connection has been configured
546 * and after the database schema has been created, upgraded or downgraded as necessary.
547 * If the database connection must be configured in some way before the schema
548 * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead.
549 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 *
551 * @param db The database.
552 */
553 public void onOpen(SQLiteDatabase db) {}
554}