blob: 8163c4d412a777dc1704e7fb699db565759676b3 [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>
Jeff Sharkeyb91eaa52019-02-19 11:09:13 -070051 *
52 * <p class="note"><strong>Note:</strong> the {@link AutoCloseable} interface was
53 * first added in the {@link android.os.Build.VERSION_CODES#Q} release.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 */
Jeff Sharkeyec5f7d12018-08-08 09:15:04 -060055public abstract class SQLiteOpenHelper implements AutoCloseable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
57
58 private final Context mContext;
Mathew Inwood41b31942018-08-10 16:00:53 +010059 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 private final String mName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061 private final int mNewVersion;
Suprabh Shukla6222176962016-10-06 14:41:35 -070062 private final int mMinimumSupportedVersion;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
Jeff Brown559d0642012-02-29 10:19:12 -080064 private SQLiteDatabase mDatabase;
65 private boolean mIsInitializing;
Fyodor Kupolovab05b142018-02-12 15:33:13 -080066 private SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067
68 /**
69 * Create a helper object to create, open, and/or manage a database.
Dan Egnor6fcc0f0732010-07-27 16:32:17 -070070 * This method always returns very quickly. The database is not actually
71 * created or opened until one of {@link #getWritableDatabase} or
72 * {@link #getReadableDatabase} is called.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 *
Fyodor Kupolov2abd2a42018-01-17 16:45:14 -080074 * @param context to use for locating paths to the the database
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 * @param name of the database file, or null for an in-memory database
76 * @param factory to use for creating cursor objects, or null for the default
77 * @param version number of the database (starting at 1); if the database is older,
Eric Hassolda5af5d62010-12-13 12:51:02 -080078 * {@link #onUpgrade} will be used to upgrade the database; if the database is
79 * newer, {@link #onDowngrade} will be used to downgrade the database
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 */
Fyodor Kupolov9d4c3d92017-11-15 16:04:24 -080081 public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
Fyodor Kupolov13a4b372017-11-07 18:45:35 -080082 @Nullable CursorFactory factory, int version) {
Jeff Brown47847f32012-03-22 19:13:11 -070083 this(context, name, factory, version, null);
Vasu Nori74f170f2010-06-01 18:06:18 -070084 }
85
86 /**
87 * Create a helper object to create, open, and/or manage a database.
88 * The database is not actually created or opened until one of
89 * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
90 *
91 * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
92 * used to handle corruption when sqlite reports database corruption.</p>
93 *
Fyodor Kupolov2abd2a42018-01-17 16:45:14 -080094 * @param context to use for locating paths to the the database
Vasu Nori74f170f2010-06-01 18:06:18 -070095 * @param name of the database file, or null for an in-memory database
96 * @param factory to use for creating cursor objects, or null for the default
97 * @param version number of the database (starting at 1); if the database is older,
Pin Ting1c423b82012-01-18 11:21:06 +080098 * {@link #onUpgrade} will be used to upgrade the database; if the database is
99 * newer, {@link #onDowngrade} will be used to downgrade the database
Vasu Nori74f170f2010-06-01 18:06:18 -0700100 * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
Jeff Brown47847f32012-03-22 19:13:11 -0700101 * corruption, or null to use the default error handler.
Vasu Nori74f170f2010-06-01 18:06:18 -0700102 */
Fyodor Kupolov9d4c3d92017-11-15 16:04:24 -0800103 public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800104 @Nullable CursorFactory factory, int version,
105 @Nullable DatabaseErrorHandler errorHandler) {
Suprabh Shukla6222176962016-10-06 14:41:35 -0700106 this(context, name, factory, version, 0, errorHandler);
107 }
108
109 /**
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800110 * Create a helper object to create, open, and/or manage a database.
111 * This method always returns very quickly. The database is not actually
112 * created or opened until one of {@link #getWritableDatabase} or
113 * {@link #getReadableDatabase} is called.
114 *
Fyodor Kupolov2abd2a42018-01-17 16:45:14 -0800115 * @param context to use for locating paths to the the database
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800116 * @param name of the database file, or null for an in-memory database
117 * @param version number of the database (starting at 1); if the database is older,
118 * {@link #onUpgrade} will be used to upgrade the database; if the database is
119 * newer, {@link #onDowngrade} will be used to downgrade the database
120 * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
121 * Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be
122 * set when the helper opens the database
123 */
Fyodor Kupolov9d4c3d92017-11-15 16:04:24 -0800124 public SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800125 @NonNull SQLiteDatabase.OpenParams openParams) {
126 this(context, name, version, 0, openParams.toBuilder());
127 }
128
129 /**
Suprabh Shukla6222176962016-10-06 14:41:35 -0700130 * Same as {@link #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler)}
131 * but also accepts an integer minimumSupportedVersion as a convenience for upgrading very old
132 * versions of this database that are no longer supported. If a database with older version that
133 * minimumSupportedVersion is found, it is simply deleted and a new database is created with the
134 * given name and version
135 *
Fyodor Kupolov2abd2a42018-01-17 16:45:14 -0800136 * @param context to use for locating paths to the the database
Suprabh Shukla6222176962016-10-06 14:41:35 -0700137 * @param name the name of the database file, null for a temporary in-memory database
138 * @param factory to use for creating cursor objects, null for default
139 * @param version the required version of the database
140 * @param minimumSupportedVersion the minimum version that is supported to be upgraded to
141 * {@code version} via {@link #onUpgrade}. If the current database version is lower
142 * than this, database is simply deleted and recreated with the version passed in
143 * {@code version}. {@link #onBeforeDelete} is called before deleting the database
144 * when this happens. This is 0 by default.
145 * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
146 * corruption, or null to use the default error handler.
147 * @see #onBeforeDelete(SQLiteDatabase)
148 * @see #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler)
149 * @see #onUpgrade(SQLiteDatabase, int, int)
150 * @hide
151 */
Fyodor Kupolov9d4c3d92017-11-15 16:04:24 -0800152 public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800153 @Nullable CursorFactory factory, int version,
154 int minimumSupportedVersion, @Nullable DatabaseErrorHandler errorHandler) {
155 this(context, name, version, minimumSupportedVersion,
156 new SQLiteDatabase.OpenParams.Builder());
157 mOpenParamsBuilder.setCursorFactory(factory);
158 mOpenParamsBuilder.setErrorHandler(errorHandler);
159 }
160
Fyodor Kupolov9d4c3d92017-11-15 16:04:24 -0800161 private SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800162 int minimumSupportedVersion,
163 @NonNull SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
Fyodor Kupolov13a4b372017-11-07 18:45:35 -0800164 Preconditions.checkNotNull(openParamsBuilder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
166
167 mContext = context;
168 mName = name;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 mNewVersion = version;
Suprabh Shukla6222176962016-10-06 14:41:35 -0700170 mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
Fyodor Kupolovab05b142018-02-12 15:33:13 -0800171 setOpenParamsBuilder(openParamsBuilder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 }
173
174 /**
Pin Ting1c423b82012-01-18 11:21:06 +0800175 * Return the name of the SQLite database being opened, as given to
Dianne Hackborn661cd522011-08-22 00:26:20 -0700176 * the constructor.
177 */
178 public String getDatabaseName() {
179 return mName;
180 }
181
182 /**
Jeff Brown47847f32012-03-22 19:13:11 -0700183 * Enables or disables the use of write-ahead logging for the database.
184 *
185 * Write-ahead logging cannot be used with read-only databases so the value of
186 * this flag is ignored if the database is opened read-only.
187 *
188 * @param enabled True if write-ahead logging should be enabled, false if it
189 * should be disabled.
190 *
191 * @see SQLiteDatabase#enableWriteAheadLogging()
192 */
193 public void setWriteAheadLoggingEnabled(boolean enabled) {
194 synchronized (this) {
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700195 if (mOpenParamsBuilder.isWriteAheadLoggingEnabled() != enabled) {
Jeff Brown47847f32012-03-22 19:13:11 -0700196 if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
197 if (enabled) {
198 mDatabase.enableWriteAheadLogging();
199 } else {
200 mDatabase.disableWriteAheadLogging();
201 }
202 }
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700203 mOpenParamsBuilder.setWriteAheadLoggingEnabled(enabled);
Jeff Brown47847f32012-03-22 19:13:11 -0700204 }
Narayan Kamathb8280432019-02-21 13:20:51 +0000205
Fyodor Kupolov692573b2018-03-06 12:34:36 -0800206 // Compatibility WAL is disabled if an app disables or enables WAL
Narayan Kamathb8280432019-02-21 13:20:51 +0000207 mOpenParamsBuilder.removeOpenFlags(SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL);
Jeff Brown47847f32012-03-22 19:13:11 -0700208 }
209 }
210
211 /**
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700212 * Configures <a href="https://sqlite.org/malloc.html#lookaside">lookaside memory allocator</a>
213 *
214 * <p>This method should be called from the constructor of the subclass,
215 * before opening the database, since lookaside memory configuration can only be changed
216 * when no connection is using it
217 *
218 * <p>SQLite default settings will be used, if this method isn't called.
219 * Use {@code setLookasideConfig(0,0)} to disable lookaside
220 *
Fyodor Kupolov05a0f0f2017-06-30 19:00:00 -0700221 * <p><strong>Note:</strong> Provided slotSize/slotCount configuration is just a recommendation.
222 * The system may choose different values depending on a device, e.g. lookaside allocations
223 * can be disabled on low-RAM devices
224 *
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700225 * @param slotSize The size in bytes of each lookaside slot.
226 * @param slotCount The total number of lookaside memory slots per database connection.
227 */
228 public void setLookasideConfig(@IntRange(from = 0) final int slotSize,
229 @IntRange(from = 0) final int slotCount) {
230 synchronized (this) {
231 if (mDatabase != null && mDatabase.isOpen()) {
232 throw new IllegalStateException(
233 "Lookaside memory config cannot be changed after opening the database");
234 }
235 mOpenParamsBuilder.setLookasideConfig(slotSize, slotCount);
236 }
237 }
238
239 /**
Fyodor Kupolovab05b142018-02-12 15:33:13 -0800240 * Sets configuration parameters that are used for opening {@link SQLiteDatabase}.
241 * <p>Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be set when
242 * opening the database
243 *
244 * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
245 * @throws IllegalStateException if the database is already open
246 */
247 public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) {
248 Preconditions.checkNotNull(openParams);
249 synchronized (this) {
250 if (mDatabase != null && mDatabase.isOpen()) {
251 throw new IllegalStateException(
252 "OpenParams cannot be set after opening the database");
253 }
254 setOpenParamsBuilder(new SQLiteDatabase.OpenParams.Builder(openParams));
255 }
256 }
257
258 private void setOpenParamsBuilder(SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
259 mOpenParamsBuilder = openParamsBuilder;
260 mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
261 }
262
263 /**
Fyodor Kupolovcf97b6b2017-07-25 14:17:33 -0700264 * Sets the maximum number of milliseconds that SQLite connection is allowed to be idle
265 * before it is closed and removed from the pool.
266 *
267 * <p>This method should be called from the constructor of the subclass,
268 * before opening the database
269 *
Makoto Onukib15186c2019-01-28 14:43:41 -0800270 * <p>DO NOT USE this method unless you fully understand the implication
271 * of what it does.
272 * See the javadoc of
273 * {@link SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)}
274 * for the details.
275 *
Fyodor Kupolovcf97b6b2017-07-25 14:17:33 -0700276 * @param idleConnectionTimeoutMs timeout in milliseconds. Use {@link Long#MAX_VALUE} value
277 * to allow unlimited idle connections.
Makoto Onukib15186c2019-01-28 14:43:41 -0800278 *
279 * @see SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)
280 *
281 * @deprecated DO NOT USE this method unless you fully understand the implication
282 * of what it does.
Fyodor Kupolovcf97b6b2017-07-25 14:17:33 -0700283 */
Makoto Onukib15186c2019-01-28 14:43:41 -0800284 @Deprecated
Fyodor Kupolovcf97b6b2017-07-25 14:17:33 -0700285 public void setIdleConnectionTimeout(@IntRange(from = 0) final long idleConnectionTimeoutMs) {
286 synchronized (this) {
287 if (mDatabase != null && mDatabase.isOpen()) {
288 throw new IllegalStateException(
289 "Connection timeout setting cannot be changed after opening the database");
290 }
291 mOpenParamsBuilder.setIdleConnectionTimeout(idleConnectionTimeoutMs);
292 }
293 }
294
295 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 * Create and/or open a database that will be used for reading and writing.
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700297 * The first time this is called, the database will be opened and
298 * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
299 * called.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 *
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700301 * <p>Once opened successfully, the database is cached, so you can
302 * call this method every time you need to write to the database.
303 * (Make sure to call {@link #close} when you no longer need the database.)
304 * Errors such as bad permissions or a full disk may cause this method
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 * to fail, but future attempts may succeed if the problem is fixed.</p>
306 *
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700307 * <p class="caution">Database upgrade may take a long time, you
308 * should not call this method from the application main thread, including
309 * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
310 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 * @throws SQLiteException if the database cannot be opened for writing
312 * @return a read/write database object valid until {@link #close} is called
313 */
Jeff Brown559d0642012-02-29 10:19:12 -0800314 public SQLiteDatabase getWritableDatabase() {
315 synchronized (this) {
316 return getDatabaseLocked(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 }
318 }
319
320 /**
321 * Create and/or open a database. This will be the same object returned by
322 * {@link #getWritableDatabase} unless some problem, such as a full disk,
323 * requires the database to be opened read-only. In that case, a read-only
324 * database object will be returned. If the problem is fixed, a future call
325 * to {@link #getWritableDatabase} may succeed, in which case the read-only
326 * database object will be closed and the read/write object will be returned
327 * in the future.
328 *
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700329 * <p class="caution">Like {@link #getWritableDatabase}, this method may
330 * take a long time to return, so you should not call it from the
331 * application main thread, including from
332 * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
333 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 * @throws SQLiteException if the database cannot be opened
335 * @return a database object valid until {@link #getWritableDatabase}
336 * or {@link #close} is called.
337 */
Jeff Brown559d0642012-02-29 10:19:12 -0800338 public SQLiteDatabase getReadableDatabase() {
339 synchronized (this) {
340 return getDatabaseLocked(false);
341 }
342 }
343
344 private SQLiteDatabase getDatabaseLocked(boolean writable) {
Vasu Noricc6f5492010-08-23 17:05:25 -0700345 if (mDatabase != null) {
346 if (!mDatabase.isOpen()) {
Jeff Brown559d0642012-02-29 10:19:12 -0800347 // Darn! The user closed the database by calling mDatabase.close().
Vasu Noricc6f5492010-08-23 17:05:25 -0700348 mDatabase = null;
Jeff Brown559d0642012-02-29 10:19:12 -0800349 } else if (!writable || !mDatabase.isReadOnly()) {
350 // The database is already open for business.
351 return mDatabase;
Vasu Noricc6f5492010-08-23 17:05:25 -0700352 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 }
354
355 if (mIsInitializing) {
Jeff Brown559d0642012-02-29 10:19:12 -0800356 throw new IllegalStateException("getDatabase called recursively");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 }
358
Jeff Brown559d0642012-02-29 10:19:12 -0800359 SQLiteDatabase db = mDatabase;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 try {
361 mIsInitializing = true;
Jeff Brown559d0642012-02-29 10:19:12 -0800362
363 if (db != null) {
364 if (writable && db.isReadOnly()) {
365 db.reopenReadWrite();
366 }
367 } else if (mName == null) {
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700368 db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
Jeff Brown559d0642012-02-29 10:19:12 -0800369 } else {
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700370 final File filePath = mContext.getDatabasePath(mName);
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700371 SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
Jeff Brown559d0642012-02-29 10:19:12 -0800372 try {
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700373 db = SQLiteDatabase.openDatabase(filePath, params);
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700374 // Keep pre-O-MR1 behavior by resetting file permissions to 660
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700375 setFilePermissionsForDb(filePath.getPath());
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700376 } catch (SQLException ex) {
Jeff Brown559d0642012-02-29 10:19:12 -0800377 if (writable) {
378 throw ex;
379 }
380 Log.e(TAG, "Couldn't open " + mName
381 + " for writing (will try read-only):", ex);
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700382 params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700383 db = SQLiteDatabase.openDatabase(filePath, params);
Jeff Brown559d0642012-02-29 10:19:12 -0800384 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 }
386
Jeff Brown96496ad2012-03-23 14:38:06 -0700387 onConfigure(db);
388
Jeff Brown559d0642012-02-29 10:19:12 -0800389 final int version = db.getVersion();
390 if (version != mNewVersion) {
391 if (db.isReadOnly()) {
392 throw new SQLiteException("Can't upgrade read-only database from version " +
393 db.getVersion() + " to " + mNewVersion + ": " + mName);
394 }
395
Suprabh Shukla6222176962016-10-06 14:41:35 -0700396 if (version > 0 && version < mMinimumSupportedVersion) {
397 File databaseFile = new File(db.getPath());
398 onBeforeDelete(db);
399 db.close();
400 if (SQLiteDatabase.deleteDatabase(databaseFile)) {
401 mIsInitializing = false;
402 return getDatabaseLocked(writable);
Jeff Brown559d0642012-02-29 10:19:12 -0800403 } else {
Suprabh Shukla6222176962016-10-06 14:41:35 -0700404 throw new IllegalStateException("Unable to delete obsolete database "
405 + mName + " with version " + version);
Jeff Brown559d0642012-02-29 10:19:12 -0800406 }
Suprabh Shukla6222176962016-10-06 14:41:35 -0700407 } else {
408 db.beginTransaction();
409 try {
410 if (version == 0) {
411 onCreate(db);
412 } else {
413 if (version > mNewVersion) {
414 onDowngrade(db, version, mNewVersion);
415 } else {
416 onUpgrade(db, version, mNewVersion);
417 }
418 }
419 db.setVersion(mNewVersion);
420 db.setTransactionSuccessful();
421 } finally {
422 db.endTransaction();
423 }
Jeff Brown559d0642012-02-29 10:19:12 -0800424 }
425 }
Jeff Brown96496ad2012-03-23 14:38:06 -0700426
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 onOpen(db);
Jeff Brown559d0642012-02-29 10:19:12 -0800428
429 if (db.isReadOnly()) {
430 Log.w(TAG, "Opened " + mName + " in read-only mode");
431 }
432
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 mDatabase = db;
Jeff Brown559d0642012-02-29 10:19:12 -0800434 return db;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 } finally {
436 mIsInitializing = false;
Jeff Brown559d0642012-02-29 10:19:12 -0800437 if (db != null && db != mDatabase) {
438 db.close();
439 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 }
441 }
442
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700443 private static void setFilePermissionsForDb(String dbPath) {
444 int perms = FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IWGRP;
445 FileUtils.setPermissions(dbPath, perms, -1, -1);
446 }
447
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 /**
449 * Close any open database object.
450 */
451 public synchronized void close() {
452 if (mIsInitializing) throw new IllegalStateException("Closed during initialization");
453
454 if (mDatabase != null && mDatabase.isOpen()) {
455 mDatabase.close();
456 mDatabase = null;
457 }
458 }
459
460 /**
Suprabh Shukla6222176962016-10-06 14:41:35 -0700461 * Called when the database connection is being configured, to enable features such as
462 * write-ahead logging or foreign key support.
Jeff Brown96496ad2012-03-23 14:38:06 -0700463 * <p>
Suprabh Shukla6222176962016-10-06 14:41:35 -0700464 * This method is called before {@link #onCreate}, {@link #onUpgrade}, {@link #onDowngrade}, or
465 * {@link #onOpen} are called. It should not modify the database except to configure the
466 * database connection as required.
467 * </p>
468 * <p>
469 * This method should only call methods that configure the parameters of the database
470 * connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
471 * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled}, {@link SQLiteDatabase#setLocale},
472 * {@link SQLiteDatabase#setMaximumSize}, or executing PRAGMA statements.
Jeff Brown96496ad2012-03-23 14:38:06 -0700473 * </p>
474 *
475 * @param db The database.
476 */
477 public void onConfigure(SQLiteDatabase db) {}
478
479 /**
Suprabh Shukla6222176962016-10-06 14:41:35 -0700480 * Called before the database is deleted when the version returned by
481 * {@link SQLiteDatabase#getVersion()} is lower than the minimum supported version passed (if at
482 * all) while creating this helper. After the database is deleted, a fresh database with the
483 * given version is created. This will be followed by {@link #onConfigure(SQLiteDatabase)} and
484 * {@link #onCreate(SQLiteDatabase)} being called with a new SQLiteDatabase object
485 *
486 * @param db the database opened with this helper
487 * @see #SQLiteOpenHelper(Context, String, CursorFactory, int, int, DatabaseErrorHandler)
488 * @hide
489 */
490 public void onBeforeDelete(SQLiteDatabase db) {
491 }
492
493 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 * Called when the database is created for the first time. This is where the
495 * creation of tables and the initial population of the tables should happen.
496 *
497 * @param db The database.
498 */
499 public abstract void onCreate(SQLiteDatabase db);
500
501 /**
502 * Called when the database needs to be upgraded. The implementation
503 * should use this method to drop tables, add tables, or do anything else it
504 * needs to upgrade to the new schema version.
505 *
Jeff Brown96496ad2012-03-23 14:38:06 -0700506 * <p>
507 * The SQLite ALTER TABLE documentation can be found
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 * <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
509 * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
510 * you can use ALTER TABLE to rename the old table, then create the new table and then
511 * populate the new table with the contents of the old table.
Jeff Brown96496ad2012-03-23 14:38:06 -0700512 * </p><p>
513 * This method executes within a transaction. If an exception is thrown, all changes
514 * will automatically be rolled back.
515 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 *
517 * @param db The database.
518 * @param oldVersion The old database version.
519 * @param newVersion The new database version.
520 */
521 public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
522
523 /**
Pin Ting1c423b82012-01-18 11:21:06 +0800524 * Called when the database needs to be downgraded. This is strictly similar to
Jeff Brown96496ad2012-03-23 14:38:06 -0700525 * {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
Eric Hassolda5af5d62010-12-13 12:51:02 -0800526 * However, this method is not abstract, so it is not mandatory for a customer to
527 * implement it. If not overridden, default implementation will reject downgrade and
528 * throws SQLiteException
529 *
Jeff Brown96496ad2012-03-23 14:38:06 -0700530 * <p>
531 * This method executes within a transaction. If an exception is thrown, all changes
532 * will automatically be rolled back.
533 * </p>
534 *
Eric Hassolda5af5d62010-12-13 12:51:02 -0800535 * @param db The database.
536 * @param oldVersion The old database version.
537 * @param newVersion The new database version.
538 */
539 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
540 throw new SQLiteException("Can't downgrade database from version " +
541 oldVersion + " to " + newVersion);
542 }
543
544 /**
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700545 * Called when the database has been opened. The implementation
546 * should check {@link SQLiteDatabase#isReadOnly} before updating the
547 * database.
Jeff Brown96496ad2012-03-23 14:38:06 -0700548 * <p>
549 * This method is called after the database connection has been configured
550 * and after the database schema has been created, upgraded or downgraded as necessary.
551 * If the database connection must be configured in some way before the schema
552 * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead.
553 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 *
555 * @param db The database.
556 */
557 public void onOpen(SQLiteDatabase db) {}
558}