1/* 2 * Copyright (C) 2016 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 androidx.sqlite.db.framework; 18 19import android.content.Context; 20import android.database.DatabaseErrorHandler; 21import android.database.sqlite.SQLiteDatabase; 22import android.database.sqlite.SQLiteOpenHelper; 23import android.os.Build; 24 25import androidx.sqlite.db.SupportSQLiteDatabase; 26import androidx.sqlite.db.SupportSQLiteOpenHelper; 27 28class FrameworkSQLiteOpenHelper implements SupportSQLiteOpenHelper { 29 private final OpenHelper mDelegate; 30 31 FrameworkSQLiteOpenHelper(Context context, String name, 32 Callback callback) { 33 mDelegate = createDelegate(context, name, callback); 34 } 35 36 private OpenHelper createDelegate(Context context, String name, Callback callback) { 37 final FrameworkSQLiteDatabase[] dbRef = new FrameworkSQLiteDatabase[1]; 38 return new OpenHelper(context, name, dbRef, callback); 39 } 40 41 @Override 42 public String getDatabaseName() { 43 return mDelegate.getDatabaseName(); 44 } 45 46 @Override 47 @androidx.annotation.RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) 48 public void setWriteAheadLoggingEnabled(boolean enabled) { 49 mDelegate.setWriteAheadLoggingEnabled(enabled); 50 } 51 52 @Override 53 public SupportSQLiteDatabase getWritableDatabase() { 54 return mDelegate.getWritableSupportDatabase(); 55 } 56 57 @Override 58 public SupportSQLiteDatabase getReadableDatabase() { 59 return mDelegate.getReadableSupportDatabase(); 60 } 61 62 @Override 63 public void close() { 64 mDelegate.close(); 65 } 66 67 static class OpenHelper extends SQLiteOpenHelper { 68 /** 69 * This is used as an Object reference so that we can access the wrapped database inside 70 * the constructor. SQLiteOpenHelper requires the error handler to be passed in the 71 * constructor. 72 */ 73 final FrameworkSQLiteDatabase[] mDbRef; 74 final Callback mCallback; 75 // see b/78359448 76 private boolean mMigrated; 77 78 OpenHelper(Context context, String name, final FrameworkSQLiteDatabase[] dbRef, 79 final Callback callback) { 80 super(context, name, null, callback.version, 81 new DatabaseErrorHandler() { 82 @Override 83 public void onCorruption(SQLiteDatabase dbObj) { 84 FrameworkSQLiteDatabase db = dbRef[0]; 85 if (db != null) { 86 callback.onCorruption(db); 87 } 88 } 89 }); 90 mCallback = callback; 91 mDbRef = dbRef; 92 } 93 94 synchronized SupportSQLiteDatabase getWritableSupportDatabase() { 95 mMigrated = false; 96 SQLiteDatabase db = super.getWritableDatabase(); 97 if (mMigrated) { 98 // there might be a connection w/ stale structure, we should re-open. 99 close(); 100 return getWritableSupportDatabase(); 101 } 102 return getWrappedDb(db); 103 } 104 105 synchronized SupportSQLiteDatabase getReadableSupportDatabase() { 106 mMigrated = false; 107 SQLiteDatabase db = super.getReadableDatabase(); 108 if (mMigrated) { 109 // there might be a connection w/ stale structure, we should re-open. 110 close(); 111 return getReadableSupportDatabase(); 112 } 113 return getWrappedDb(db); 114 } 115 116 FrameworkSQLiteDatabase getWrappedDb(SQLiteDatabase sqLiteDatabase) { 117 FrameworkSQLiteDatabase dbRef = mDbRef[0]; 118 if (dbRef == null) { 119 dbRef = new FrameworkSQLiteDatabase(sqLiteDatabase); 120 mDbRef[0] = dbRef; 121 } 122 return mDbRef[0]; 123 } 124 125 @Override 126 public void onCreate(SQLiteDatabase sqLiteDatabase) { 127 mCallback.onCreate(getWrappedDb(sqLiteDatabase)); 128 } 129 130 @Override 131 public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { 132 mMigrated = true; 133 mCallback.onUpgrade(getWrappedDb(sqLiteDatabase), oldVersion, newVersion); 134 } 135 136 @Override 137 public void onConfigure(SQLiteDatabase db) { 138 mCallback.onConfigure(getWrappedDb(db)); 139 } 140 141 @Override 142 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 143 mMigrated = true; 144 mCallback.onDowngrade(getWrappedDb(db), oldVersion, newVersion); 145 } 146 147 @Override 148 public void onOpen(SQLiteDatabase db) { 149 if (!mMigrated) { 150 // if we've migrated, we'll re-open the db so we should not call the callback. 151 mCallback.onOpen(getWrappedDb(db)); 152 } 153 } 154 155 @Override 156 public synchronized void close() { 157 super.close(); 158 mDbRef[0] = null; 159 } 160 } 161} 162