blob: 62cec0e1aa92005e046413775b42cadcea42c3fd [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 Onuki1a904792019-02-28 12:04:52 -0800270 * <p><b>DO NOT USE</b> this method.
271 * This feature has negative side effects that are very hard to foresee.
Makoto Onukib15186c2019-01-28 14:43:41 -0800272 * 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 *
Makoto Onuki1a904792019-02-28 12:04:52 -0800281 * @deprecated DO NOT USE this method. See the javadoc of
282 * {@link SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)}
283 * for the details.
Fyodor Kupolovcf97b6b2017-07-25 14:17:33 -0700284 */
Makoto Onukib15186c2019-01-28 14:43:41 -0800285 @Deprecated
Fyodor Kupolovcf97b6b2017-07-25 14:17:33 -0700286 public void setIdleConnectionTimeout(@IntRange(from = 0) final long idleConnectionTimeoutMs) {
287 synchronized (this) {
288 if (mDatabase != null && mDatabase.isOpen()) {
289 throw new IllegalStateException(
290 "Connection timeout setting cannot be changed after opening the database");
291 }
292 mOpenParamsBuilder.setIdleConnectionTimeout(idleConnectionTimeoutMs);
293 }
294 }
295
296 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 * Create and/or open a database that will be used for reading and writing.
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700298 * The first time this is called, the database will be opened and
299 * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
300 * called.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 *
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700302 * <p>Once opened successfully, the database is cached, so you can
303 * call this method every time you need to write to the database.
304 * (Make sure to call {@link #close} when you no longer need the database.)
305 * Errors such as bad permissions or a full disk may cause this method
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 * to fail, but future attempts may succeed if the problem is fixed.</p>
307 *
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700308 * <p class="caution">Database upgrade may take a long time, you
309 * should not call this method from the application main thread, including
310 * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
311 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 * @throws SQLiteException if the database cannot be opened for writing
313 * @return a read/write database object valid until {@link #close} is called
314 */
Jeff Brown559d0642012-02-29 10:19:12 -0800315 public SQLiteDatabase getWritableDatabase() {
316 synchronized (this) {
317 return getDatabaseLocked(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 }
319 }
320
321 /**
322 * Create and/or open a database. This will be the same object returned by
323 * {@link #getWritableDatabase} unless some problem, such as a full disk,
324 * requires the database to be opened read-only. In that case, a read-only
325 * database object will be returned. If the problem is fixed, a future call
326 * to {@link #getWritableDatabase} may succeed, in which case the read-only
327 * database object will be closed and the read/write object will be returned
328 * in the future.
329 *
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700330 * <p class="caution">Like {@link #getWritableDatabase}, this method may
331 * take a long time to return, so you should not call it from the
332 * application main thread, including from
333 * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
334 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 * @throws SQLiteException if the database cannot be opened
336 * @return a database object valid until {@link #getWritableDatabase}
337 * or {@link #close} is called.
338 */
Jeff Brown559d0642012-02-29 10:19:12 -0800339 public SQLiteDatabase getReadableDatabase() {
340 synchronized (this) {
341 return getDatabaseLocked(false);
342 }
343 }
344
345 private SQLiteDatabase getDatabaseLocked(boolean writable) {
Vasu Noricc6f5492010-08-23 17:05:25 -0700346 if (mDatabase != null) {
347 if (!mDatabase.isOpen()) {
Jeff Brown559d0642012-02-29 10:19:12 -0800348 // Darn! The user closed the database by calling mDatabase.close().
Vasu Noricc6f5492010-08-23 17:05:25 -0700349 mDatabase = null;
Jeff Brown559d0642012-02-29 10:19:12 -0800350 } else if (!writable || !mDatabase.isReadOnly()) {
351 // The database is already open for business.
352 return mDatabase;
Vasu Noricc6f5492010-08-23 17:05:25 -0700353 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 }
355
356 if (mIsInitializing) {
Jeff Brown559d0642012-02-29 10:19:12 -0800357 throw new IllegalStateException("getDatabase called recursively");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 }
359
Jeff Brown559d0642012-02-29 10:19:12 -0800360 SQLiteDatabase db = mDatabase;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 try {
362 mIsInitializing = true;
Jeff Brown559d0642012-02-29 10:19:12 -0800363
364 if (db != null) {
365 if (writable && db.isReadOnly()) {
366 db.reopenReadWrite();
367 }
368 } else if (mName == null) {
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700369 db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
Jeff Brown559d0642012-02-29 10:19:12 -0800370 } else {
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700371 final File filePath = mContext.getDatabasePath(mName);
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700372 SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
Jeff Brown559d0642012-02-29 10:19:12 -0800373 try {
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700374 db = SQLiteDatabase.openDatabase(filePath, params);
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700375 // Keep pre-O-MR1 behavior by resetting file permissions to 660
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700376 setFilePermissionsForDb(filePath.getPath());
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700377 } catch (SQLException ex) {
Jeff Brown559d0642012-02-29 10:19:12 -0800378 if (writable) {
379 throw ex;
380 }
381 Log.e(TAG, "Couldn't open " + mName
382 + " for writing (will try read-only):", ex);
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700383 params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
Fyodor Kupolov76436c02017-08-03 17:56:44 -0700384 db = SQLiteDatabase.openDatabase(filePath, params);
Jeff Brown559d0642012-02-29 10:19:12 -0800385 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 }
387
Jeff Brown96496ad2012-03-23 14:38:06 -0700388 onConfigure(db);
389
Jeff Brown559d0642012-02-29 10:19:12 -0800390 final int version = db.getVersion();
391 if (version != mNewVersion) {
392 if (db.isReadOnly()) {
393 throw new SQLiteException("Can't upgrade read-only database from version " +
394 db.getVersion() + " to " + mNewVersion + ": " + mName);
395 }
396
Suprabh Shukla6222176962016-10-06 14:41:35 -0700397 if (version > 0 && version < mMinimumSupportedVersion) {
398 File databaseFile = new File(db.getPath());
399 onBeforeDelete(db);
400 db.close();
401 if (SQLiteDatabase.deleteDatabase(databaseFile)) {
402 mIsInitializing = false;
403 return getDatabaseLocked(writable);
Jeff Brown559d0642012-02-29 10:19:12 -0800404 } else {
Suprabh Shukla6222176962016-10-06 14:41:35 -0700405 throw new IllegalStateException("Unable to delete obsolete database "
406 + mName + " with version " + version);
Jeff Brown559d0642012-02-29 10:19:12 -0800407 }
Suprabh Shukla6222176962016-10-06 14:41:35 -0700408 } else {
409 db.beginTransaction();
410 try {
411 if (version == 0) {
412 onCreate(db);
413 } else {
414 if (version > mNewVersion) {
415 onDowngrade(db, version, mNewVersion);
416 } else {
417 onUpgrade(db, version, mNewVersion);
418 }
419 }
420 db.setVersion(mNewVersion);
421 db.setTransactionSuccessful();
422 } finally {
423 db.endTransaction();
424 }
Jeff Brown559d0642012-02-29 10:19:12 -0800425 }
426 }
Jeff Brown96496ad2012-03-23 14:38:06 -0700427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 onOpen(db);
Jeff Brown559d0642012-02-29 10:19:12 -0800429
430 if (db.isReadOnly()) {
431 Log.w(TAG, "Opened " + mName + " in read-only mode");
432 }
433
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 mDatabase = db;
Jeff Brown559d0642012-02-29 10:19:12 -0800435 return db;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 } finally {
437 mIsInitializing = false;
Jeff Brown559d0642012-02-29 10:19:12 -0800438 if (db != null && db != mDatabase) {
439 db.close();
440 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 }
442 }
443
Fyodor Kupolovd3b0c7e2017-06-20 11:51:55 -0700444 private static void setFilePermissionsForDb(String dbPath) {
445 int perms = FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IWGRP;
446 FileUtils.setPermissions(dbPath, perms, -1, -1);
447 }
448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 /**
450 * Close any open database object.
451 */
452 public synchronized void close() {
453 if (mIsInitializing) throw new IllegalStateException("Closed during initialization");
454
455 if (mDatabase != null && mDatabase.isOpen()) {
456 mDatabase.close();
457 mDatabase = null;
458 }
459 }
460
461 /**
Suprabh Shukla6222176962016-10-06 14:41:35 -0700462 * Called when the database connection is being configured, to enable features such as
463 * write-ahead logging or foreign key support.
Jeff Brown96496ad2012-03-23 14:38:06 -0700464 * <p>
Suprabh Shukla6222176962016-10-06 14:41:35 -0700465 * This method is called before {@link #onCreate}, {@link #onUpgrade}, {@link #onDowngrade}, or
466 * {@link #onOpen} are called. It should not modify the database except to configure the
467 * database connection as required.
468 * </p>
469 * <p>
470 * This method should only call methods that configure the parameters of the database
471 * connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
472 * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled}, {@link SQLiteDatabase#setLocale},
473 * {@link SQLiteDatabase#setMaximumSize}, or executing PRAGMA statements.
Jeff Brown96496ad2012-03-23 14:38:06 -0700474 * </p>
475 *
476 * @param db The database.
477 */
478 public void onConfigure(SQLiteDatabase db) {}
479
480 /**
Suprabh Shukla6222176962016-10-06 14:41:35 -0700481 * Called before the database is deleted when the version returned by
482 * {@link SQLiteDatabase#getVersion()} is lower than the minimum supported version passed (if at
483 * all) while creating this helper. After the database is deleted, a fresh database with the
484 * given version is created. This will be followed by {@link #onConfigure(SQLiteDatabase)} and
485 * {@link #onCreate(SQLiteDatabase)} being called with a new SQLiteDatabase object
486 *
487 * @param db the database opened with this helper
488 * @see #SQLiteOpenHelper(Context, String, CursorFactory, int, int, DatabaseErrorHandler)
489 * @hide
490 */
491 public void onBeforeDelete(SQLiteDatabase db) {
492 }
493
494 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 * Called when the database is created for the first time. This is where the
496 * creation of tables and the initial population of the tables should happen.
497 *
498 * @param db The database.
499 */
500 public abstract void onCreate(SQLiteDatabase db);
501
502 /**
503 * Called when the database needs to be upgraded. The implementation
504 * should use this method to drop tables, add tables, or do anything else it
505 * needs to upgrade to the new schema version.
506 *
Jeff Brown96496ad2012-03-23 14:38:06 -0700507 * <p>
508 * The SQLite ALTER TABLE documentation can be found
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 * <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
510 * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
511 * you can use ALTER TABLE to rename the old table, then create the new table and then
512 * populate the new table with the contents of the old table.
Jeff Brown96496ad2012-03-23 14:38:06 -0700513 * </p><p>
514 * This method executes within a transaction. If an exception is thrown, all changes
515 * will automatically be rolled back.
516 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 *
518 * @param db The database.
519 * @param oldVersion The old database version.
520 * @param newVersion The new database version.
521 */
522 public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
523
524 /**
Pin Ting1c423b82012-01-18 11:21:06 +0800525 * Called when the database needs to be downgraded. This is strictly similar to
Jeff Brown96496ad2012-03-23 14:38:06 -0700526 * {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
Eric Hassolda5af5d62010-12-13 12:51:02 -0800527 * However, this method is not abstract, so it is not mandatory for a customer to
528 * implement it. If not overridden, default implementation will reject downgrade and
529 * throws SQLiteException
530 *
Jeff Brown96496ad2012-03-23 14:38:06 -0700531 * <p>
532 * This method executes within a transaction. If an exception is thrown, all changes
533 * will automatically be rolled back.
534 * </p>
535 *
Eric Hassolda5af5d62010-12-13 12:51:02 -0800536 * @param db The database.
537 * @param oldVersion The old database version.
538 * @param newVersion The new database version.
539 */
540 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
541 throw new SQLiteException("Can't downgrade database from version " +
542 oldVersion + " to " + newVersion);
543 }
544
545 /**
Dan Egnor6fcc0f0732010-07-27 16:32:17 -0700546 * Called when the database has been opened. The implementation
547 * should check {@link SQLiteDatabase#isReadOnly} before updating the
548 * database.
Jeff Brown96496ad2012-03-23 14:38:06 -0700549 * <p>
550 * This method is called after the database connection has been configured
551 * and after the database schema has been created, upgraded or downgraded as necessary.
552 * If the database connection must be configured in some way before the schema
553 * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead.
554 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 *
556 * @param db The database.
557 */
558 public void onOpen(SQLiteDatabase db) {}
559}