• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.database.sqlite;
18 
19 import android.annotation.IntRange;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.compat.annotation.UnsupportedAppUsage;
23 import android.content.Context;
24 import android.database.DatabaseErrorHandler;
25 import android.database.SQLException;
26 import android.database.sqlite.SQLiteDatabase.CursorFactory;
27 import android.os.FileUtils;
28 import android.util.ArrayMap;
29 import android.util.Log;
30 
31 import com.android.internal.annotations.GuardedBy;
32 
33 import java.io.File;
34 import java.io.IOException;
35 import java.util.Objects;
36 import java.util.concurrent.ConcurrentHashMap;
37 
38 /**
39  * A helper class to manage database creation and version management.
40  *
41  * <p>You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
42  * optionally {@link #onOpen}, and this class takes care of opening the database
43  * if it exists, creating it if it does not, and upgrading it as necessary.
44  * Transactions are used to make sure the database is always in a sensible state.
45  *
46  * <p>This class makes it easy for {@link android.content.ContentProvider}
47  * implementations to defer opening and upgrading the database until first use,
48  * to avoid blocking application startup with long-running database upgrades.
49  *
50  * <p>For an example, see the NotePadProvider class in the NotePad sample application,
51  * in the <em>samples/</em> directory of the SDK.</p>
52  *
53  * <p class="note"><strong>Note:</strong> this class assumes
54  * monotonically increasing version numbers for upgrades.</p>
55  *
56  * <p class="note"><strong>Note:</strong> the {@link AutoCloseable} interface was
57  * first added in the {@link android.os.Build.VERSION_CODES#Q} release.</p>
58  */
59 public abstract class SQLiteOpenHelper implements AutoCloseable {
60     private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
61 
62     // Every database file has a lock, saved in this map.  The lock is held while the database is
63     // opened.
64     private static final ConcurrentHashMap<String, Object> sDbLock = new ConcurrentHashMap<>();
65 
66     // The lock that this open helper instance must hold when the database is opened.
67     private final Object mLock;
68 
69     private final Context mContext;
70     @UnsupportedAppUsage
71     private final String mName;
72     private final int mNewVersion;
73     private final int mMinimumSupportedVersion;
74 
75     private SQLiteDatabase mDatabase;
76     private boolean mIsInitializing;
77     private SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder;
78 
79     /**
80      * Create a helper object to create, open, and/or manage a database.
81      * This method always returns very quickly.  The database is not actually
82      * created or opened until one of {@link #getWritableDatabase} or
83      * {@link #getReadableDatabase} is called.
84      *
85      * @param context to use for locating paths to the the database
86      * @param name of the database file, or null for an in-memory database
87      * @param factory to use for creating cursor objects, or null for the default
88      * @param version number of the database (starting at 1); if the database is older,
89      *     {@link #onUpgrade} will be used to upgrade the database; if the database is
90      *     newer, {@link #onDowngrade} will be used to downgrade the database
91      */
SQLiteOpenHelper(@ullable Context context, @Nullable String name, @Nullable CursorFactory factory, int version)92     public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
93             @Nullable CursorFactory factory, int version) {
94         this(context, name, factory, version, null);
95     }
96 
97     /**
98      * Create a helper object to create, open, and/or manage a database.
99      * The database is not actually created or opened until one of
100      * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
101      *
102      * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
103      * used to handle corruption when sqlite reports database corruption.</p>
104      *
105      * @param context to use for locating paths to the the database
106      * @param name of the database file, or null for an in-memory database
107      * @param factory to use for creating cursor objects, or null for the default
108      * @param version number of the database (starting at 1); if the database is older,
109      *     {@link #onUpgrade} will be used to upgrade the database; if the database is
110      *     newer, {@link #onDowngrade} will be used to downgrade the database
111      * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
112      * corruption, or null to use the default error handler.
113      */
SQLiteOpenHelper(@ullable Context context, @Nullable String name, @Nullable CursorFactory factory, int version, @Nullable DatabaseErrorHandler errorHandler)114     public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
115             @Nullable CursorFactory factory, int version,
116             @Nullable DatabaseErrorHandler errorHandler) {
117         this(context, name, factory, version, 0, errorHandler);
118     }
119 
120     /**
121      * Create a helper object to create, open, and/or manage a database.
122      * This method always returns very quickly.  The database is not actually
123      * created or opened until one of {@link #getWritableDatabase} or
124      * {@link #getReadableDatabase} is called.
125      *
126      * @param context to use for locating paths to the the database
127      * @param name of the database file, or null for an in-memory database
128      * @param version number of the database (starting at 1); if the database is older,
129      *     {@link #onUpgrade} will be used to upgrade the database; if the database is
130      *     newer, {@link #onDowngrade} will be used to downgrade the database
131      * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
132      *        Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be
133      *        set when the helper opens the database
134      */
SQLiteOpenHelper(@ullable Context context, @Nullable String name, int version, @NonNull SQLiteDatabase.OpenParams openParams)135     public SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
136             @NonNull SQLiteDatabase.OpenParams openParams) {
137         this(context, name, version, 0, openParams.toBuilder());
138     }
139 
140     /**
141      * Same as {@link #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler)}
142      * but also accepts an integer minimumSupportedVersion as a convenience for upgrading very old
143      * versions of this database that are no longer supported. If a database with older version that
144      * minimumSupportedVersion is found, it is simply deleted and a new database is created with the
145      * given name and version
146      *
147      * @param context to use for locating paths to the the database
148      * @param name the name of the database file, null for a temporary in-memory database
149      * @param factory to use for creating cursor objects, null for default
150      * @param version the required version of the database
151      * @param minimumSupportedVersion the minimum version that is supported to be upgraded to
152      *            {@code version} via {@link #onUpgrade}. If the current database version is lower
153      *            than this, database is simply deleted and recreated with the version passed in
154      *            {@code version}. {@link #onBeforeDelete} is called before deleting the database
155      *            when this happens. This is 0 by default.
156      * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
157      *            corruption, or null to use the default error handler.
158      * @see #onBeforeDelete(SQLiteDatabase)
159      * @see #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler)
160      * @see #onUpgrade(SQLiteDatabase, int, int)
161      * @hide
162      */
SQLiteOpenHelper(@ullable Context context, @Nullable String name, @Nullable CursorFactory factory, int version, int minimumSupportedVersion, @Nullable DatabaseErrorHandler errorHandler)163     public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
164             @Nullable CursorFactory factory, int version,
165             int minimumSupportedVersion, @Nullable DatabaseErrorHandler errorHandler) {
166         this(context, name, version, minimumSupportedVersion,
167                 new SQLiteDatabase.OpenParams.Builder());
168         mOpenParamsBuilder.setCursorFactory(factory);
169         mOpenParamsBuilder.setErrorHandler(errorHandler);
170     }
171 
SQLiteOpenHelper(@ullable Context context, @Nullable String name, int version, int minimumSupportedVersion, @NonNull SQLiteDatabase.OpenParams.Builder openParamsBuilder)172     private SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
173             int minimumSupportedVersion,
174             @NonNull SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
175         Objects.requireNonNull(openParamsBuilder);
176         if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
177 
178         mContext = context;
179         mName = name;
180         mNewVersion = version;
181         mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
182         setOpenParamsBuilder(openParamsBuilder);
183 
184         Object lock = null;
185         if (!Flags.concurrentOpenHelper() || mName == null) {
186             lock = new Object();
187         } else {
188             lock = sDbLock.computeIfAbsent(mName, (String k) -> new Object());
189         }
190         mLock = lock;
191     }
192 
193     /**
194      * Return the name of the SQLite database being opened, as given to
195      * the constructor.
196      */
getDatabaseName()197     public String getDatabaseName() {
198         return mName;
199     }
200 
201     /**
202      * Enables or disables the use of write-ahead logging for the database.
203      *
204      * Write-ahead logging cannot be used with read-only databases so the value of
205      * this flag is ignored if the database is opened read-only.
206      *
207      * @param enabled True if write-ahead logging should be enabled, false if it
208      * should be disabled.
209      *
210      * @see SQLiteDatabase#enableWriteAheadLogging()
211      */
setWriteAheadLoggingEnabled(boolean enabled)212     public void setWriteAheadLoggingEnabled(boolean enabled) {
213         synchronized (this) {
214             if (mOpenParamsBuilder.isWriteAheadLoggingEnabled() != enabled) {
215                 if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
216                     if (enabled) {
217                         mDatabase.enableWriteAheadLogging();
218                     } else {
219                         mDatabase.disableWriteAheadLogging();
220                     }
221                 }
222                 mOpenParamsBuilder.setWriteAheadLoggingEnabled(enabled);
223             }
224 
225             // Compatibility WAL is disabled if an app disables or enables WAL
226             mOpenParamsBuilder.removeOpenFlags(SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL);
227         }
228     }
229 
230     /**
231      * Configures <a href="https://sqlite.org/malloc.html#lookaside">lookaside memory allocator</a>
232      *
233      * <p>This method should be called from the constructor of the subclass,
234      * before opening the database, since lookaside memory configuration can only be changed
235      * when no connection is using it
236      *
237      * <p>SQLite default settings will be used, if this method isn't called.
238      * Use {@code setLookasideConfig(0,0)} to disable lookaside
239      *
240      * <p><strong>Note:</strong> Provided slotSize/slotCount configuration is just a recommendation.
241      * The system may choose different values depending on a device, e.g. lookaside allocations
242      * can be disabled on low-RAM devices
243      *
244      * @param slotSize The size in bytes of each lookaside slot.
245      * @param slotCount The total number of lookaside memory slots per database connection.
246      */
setLookasideConfig(@ntRangefrom = 0) final int slotSize, @IntRange(from = 0) final int slotCount)247     public void setLookasideConfig(@IntRange(from = 0) final int slotSize,
248             @IntRange(from = 0) final int slotCount) {
249         synchronized (this) {
250             if (mDatabase != null && mDatabase.isOpen()) {
251                 throw new IllegalStateException(
252                         "Lookaside memory config cannot be changed after opening the database");
253             }
254             mOpenParamsBuilder.setLookasideConfig(slotSize, slotCount);
255         }
256     }
257 
258     /**
259      * Sets configuration parameters that are used for opening {@link SQLiteDatabase}.
260      * <p>Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be set when
261      * opening the database
262      *
263      * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
264      * @throws IllegalStateException if the database is already open
265      */
setOpenParams(@onNull SQLiteDatabase.OpenParams openParams)266     public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) {
267         Objects.requireNonNull(openParams);
268         synchronized (this) {
269             if (mDatabase != null && mDatabase.isOpen()) {
270                 throw new IllegalStateException(
271                         "OpenParams cannot be set after opening the database");
272             }
273             setOpenParamsBuilder(new SQLiteDatabase.OpenParams.Builder(openParams));
274         }
275     }
276 
setOpenParamsBuilder(SQLiteDatabase.OpenParams.Builder openParamsBuilder)277     private void setOpenParamsBuilder(SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
278         mOpenParamsBuilder = openParamsBuilder;
279         mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
280     }
281 
282     /**
283      * Sets the maximum number of milliseconds that SQLite connection is allowed to be idle
284      * before it is closed and removed from the pool.
285      *
286      * <p>This method should be called from the constructor of the subclass,
287      * before opening the database
288      *
289      * <p><b>DO NOT USE</b> this method.
290      * This feature has negative side effects that are very hard to foresee.
291      * See the javadoc of
292      * {@link SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)}
293      * for the details.
294      *
295      * @param idleConnectionTimeoutMs timeout in milliseconds. Use {@link Long#MAX_VALUE} value
296      * to allow unlimited idle connections.
297      *
298      * @see SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)
299      *
300      * @deprecated DO NOT USE this method. See the javadoc of
301      * {@link SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)}
302      * for the details.
303      */
304     @Deprecated
setIdleConnectionTimeout(@ntRangefrom = 0) final long idleConnectionTimeoutMs)305     public void setIdleConnectionTimeout(@IntRange(from = 0) final long idleConnectionTimeoutMs) {
306         synchronized (this) {
307             if (mDatabase != null && mDatabase.isOpen()) {
308                 throw new IllegalStateException(
309                         "Connection timeout setting cannot be changed after opening the database");
310             }
311             mOpenParamsBuilder.setIdleConnectionTimeout(idleConnectionTimeoutMs);
312         }
313     }
314 
315     /**
316      * Create and/or open a database that will be used for reading and writing.
317      * The first time this is called, the database will be opened and
318      * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
319      * called.
320      *
321      * <p>Once opened successfully, the database is cached, so you can
322      * call this method every time you need to write to the database.
323      * (Make sure to call {@link #close} when you no longer need the database.)
324      * Errors such as bad permissions or a full disk may cause this method
325      * to fail, but future attempts may succeed if the problem is fixed.</p>
326      *
327      * <p class="caution">Database upgrade may take a long time, you
328      * should not call this method from the application main thread, including
329      * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
330      *
331      * @throws SQLiteException if the database cannot be opened for writing
332      * @return a read/write database object valid until {@link #close} is called
333      */
getWritableDatabase()334     public SQLiteDatabase getWritableDatabase() {
335         synchronized (this) {
336             return getDatabaseLocked(true);
337         }
338     }
339 
340     /**
341      * Create and/or open a database.  This will be the same object returned by
342      * {@link #getWritableDatabase} unless some problem, such as a full disk,
343      * requires the database to be opened read-only.  In that case, a read-only
344      * database object will be returned.  If the problem is fixed, a future call
345      * to {@link #getWritableDatabase} may succeed, in which case the read-only
346      * database object will be closed and the read/write object will be returned
347      * in the future.
348      *
349      * <p class="caution">Like {@link #getWritableDatabase}, this method may
350      * take a long time to return, so you should not call it from the
351      * application main thread, including from
352      * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
353      *
354      * @throws SQLiteException if the database cannot be opened
355      * @return a database object valid until {@link #getWritableDatabase}
356      *     or {@link #close} is called.
357      */
getReadableDatabase()358     public SQLiteDatabase getReadableDatabase() {
359         synchronized (this) {
360             return getDatabaseLocked(false);
361         }
362     }
363 
getDatabaseLocked(boolean writable)364     private SQLiteDatabase getDatabaseLocked(boolean writable) {
365         if (mDatabase != null) {
366             if (!mDatabase.isOpen()) {
367                 // Darn!  The user closed the database by calling mDatabase.close().
368                 mDatabase = null;
369             } else if (!writable || !mDatabase.isReadOnly()) {
370                 // The database is already open for business.
371                 return mDatabase;
372             }
373         }
374 
375         if (mIsInitializing) {
376             throw new IllegalStateException("getDatabase called recursively");
377         }
378 
379         SQLiteDatabase db = mDatabase;
380         try {
381             synchronized (mLock) {
382                 mIsInitializing = true;
383 
384                 if (db != null) {
385                     if (writable && db.isReadOnly()) {
386                         db.reopenReadWrite();
387                     }
388                 } else if (mName == null) {
389                     db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
390                 } else {
391                     final File filePath = mContext.getDatabasePath(mName);
392                     SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
393                     try {
394                         db = SQLiteDatabase.openDatabase(filePath, params);
395                         // Keep pre-O-MR1 behavior by resetting file permissions to 660
396                         setFilePermissionsForDb(filePath.getPath());
397                     } catch (SQLException ex) {
398                         if (writable) {
399                             throw ex;
400                         }
401                         Log.e(TAG, "Couldn't open database for writing (will try read-only):", ex);
402                         params = params.toBuilder()
403                                  .addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
404                         db = SQLiteDatabase.openDatabase(filePath, params);
405                     }
406                 }
407 
408                 onConfigure(db);
409 
410                 final int version = db.getVersion();
411                 if (version != mNewVersion) {
412                     if (db.isReadOnly()) {
413                         throw new SQLiteException("Can't upgrade read-only database from version "
414                                 + db.getVersion() + " to " + mNewVersion + ": " + mName);
415                     }
416 
417                     if (version > 0 && version < mMinimumSupportedVersion) {
418                         File databaseFile = new File(db.getPath());
419                         onBeforeDelete(db);
420                         db.close();
421                         if (SQLiteDatabase.deleteDatabase(databaseFile)) {
422                             mIsInitializing = false;
423                             return getDatabaseLocked(writable);
424                         } else {
425                             throw new IllegalStateException("Unable to delete obsolete database "
426                                     + mName + " with version " + version);
427                         }
428                     } else {
429                         db.beginTransaction();
430                         try {
431                             if (version == 0) {
432                                 onCreate(db);
433                             } else {
434                                 if (version > mNewVersion) {
435                                     onDowngrade(db, version, mNewVersion);
436                                 } else {
437                                     onUpgrade(db, version, mNewVersion);
438                                 }
439                             }
440                             db.setVersion(mNewVersion);
441                             db.setTransactionSuccessful();
442                         } finally {
443                             db.endTransaction();
444                         }
445                     }
446                 }
447 
448                 onOpen(db);
449                 mDatabase = db;
450                 return db;
451             }
452         } finally {
453             mIsInitializing = false;
454             if (db != null && db != mDatabase) {
455                 db.close();
456             }
457         }
458     }
459 
setFilePermissionsForDb(String dbPath)460     private static void setFilePermissionsForDb(String dbPath) {
461         int perms = FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IWGRP;
462         FileUtils.setPermissions(dbPath, perms, -1, -1);
463     }
464 
465     /**
466      * Close any open database object.
467      */
close()468     public synchronized void close() {
469         if (mIsInitializing) throw new IllegalStateException("Closed during initialization");
470 
471         if (mDatabase != null && mDatabase.isOpen()) {
472             mDatabase.close();
473             mDatabase = null;
474         }
475     }
476 
477     /**
478      * Called when the database connection is being configured, to enable features such as
479      * write-ahead logging or foreign key support.
480      * <p>
481      * This method is called before {@link #onCreate}, {@link #onUpgrade}, {@link #onDowngrade}, or
482      * {@link #onOpen} are called. It should not modify the database except to configure the
483      * database connection as required.
484      * </p>
485      * <p>
486      * This method should only call methods that configure the parameters of the database
487      * connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
488      * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled}, {@link SQLiteDatabase#setLocale},
489      * {@link SQLiteDatabase#setMaximumSize}, or executing PRAGMA statements.
490      * </p>
491      *
492      * @param db The database.
493      */
onConfigure(SQLiteDatabase db)494     public void onConfigure(SQLiteDatabase db) {}
495 
496     /**
497      * Called before the database is deleted when the version returned by
498      * {@link SQLiteDatabase#getVersion()} is lower than the minimum supported version passed (if at
499      * all) while creating this helper. After the database is deleted, a fresh database with the
500      * given version is created. This will be followed by {@link #onConfigure(SQLiteDatabase)} and
501      * {@link #onCreate(SQLiteDatabase)} being called with a new SQLiteDatabase object
502      *
503      * @param db the database opened with this helper
504      * @see #SQLiteOpenHelper(Context, String, CursorFactory, int, int, DatabaseErrorHandler)
505      * @hide
506      */
onBeforeDelete(SQLiteDatabase db)507     public void onBeforeDelete(SQLiteDatabase db) {
508     }
509 
510     /**
511      * Called when the database is created for the first time. This is where the
512      * creation of tables and the initial population of the tables should happen.
513      *
514      * @param db The database.
515      */
onCreate(SQLiteDatabase db)516     public abstract void onCreate(SQLiteDatabase db);
517 
518     /**
519      * Called when the database needs to be upgraded. The implementation
520      * should use this method to drop tables, add tables, or do anything else it
521      * needs to upgrade to the new schema version.
522      *
523      * <p>
524      * The SQLite ALTER TABLE documentation can be found
525      * <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
526      * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
527      * you can use ALTER TABLE to rename the old table, then create the new table and then
528      * populate the new table with the contents of the old table.
529      * </p><p>
530      * This method executes within a transaction.  If an exception is thrown, all changes
531      * will automatically be rolled back.
532      * </p>
533      * <p>
534      * <em>Important:</em> You should NOT modify an existing migration step from version X to X+1
535      * once a build has been released containing that migration step.  If a migration step has an
536      * error and it runs on a device, the step will NOT re-run itself in the future if a fix is made
537      * to the migration step.</p>
538      * <p>For example, suppose a migration step renames a database column from {@code foo} to
539      * {@code bar} when the name should have been {@code baz}.  If that migration step is released
540      * in a build and runs on a user's device, the column will be renamed to {@code bar}.  If the
541      * developer subsequently edits this same migration step to change the name to {@code baz} as
542      * intended, the user devices which have already run this step will still have the name
543      * {@code bar}.  Instead, a NEW migration step should be created to correct the error and rename
544      * {@code bar} to {@code baz}, ensuring the error is corrected on devices which have already run
545      * the migration step with the error.</p>
546      *
547      * @param db The database.
548      * @param oldVersion The old database version.
549      * @param newVersion The new database version.
550      */
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)551     public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
552 
553     /**
554      * Called when the database needs to be downgraded. This is strictly similar to
555      * {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
556      * However, this method is not abstract, so it is not mandatory for a customer to
557      * implement it. If not overridden, default implementation will reject downgrade and
558      * throws SQLiteException
559      *
560      * <p>
561      * This method executes within a transaction.  If an exception is thrown, all changes
562      * will automatically be rolled back.
563      * </p>
564      *
565      * @param db The database.
566      * @param oldVersion The old database version.
567      * @param newVersion The new database version.
568      */
onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)569     public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
570         throw new SQLiteException("Can't downgrade database from version " +
571                 oldVersion + " to " + newVersion);
572     }
573 
574     /**
575      * Called when the database has been opened.  The implementation
576      * should check {@link SQLiteDatabase#isReadOnly} before updating the
577      * database.
578      * <p>
579      * This method is called after the database connection has been configured
580      * and after the database schema has been created, upgraded or downgraded as necessary.
581      * If the database connection must be configured in some way before the schema
582      * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead.
583      * </p>
584      *
585      * @param db The database.
586      */
onOpen(SQLiteDatabase db)587     public void onOpen(SQLiteDatabase db) {}
588 }
589