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