1package com.android.launcher3.util; 2 3import android.content.ContentValues; 4import android.content.Context; 5import android.database.Cursor; 6import android.database.sqlite.SQLiteDatabase; 7import android.database.sqlite.SQLiteException; 8import android.database.sqlite.SQLiteFullException; 9import android.database.sqlite.SQLiteOpenHelper; 10import android.util.Log; 11 12import com.android.launcher3.Utilities; 13import com.android.launcher3.config.ProviderConfig; 14 15/** 16 * An extension of {@link SQLiteOpenHelper} with utility methods for a single table cache DB. 17 * Any exception during write operations are ignored, and any version change causes a DB reset. 18 */ 19public abstract class SQLiteCacheHelper { 20 private static final String TAG = "SQLiteCacheHelper"; 21 22 private static final boolean NO_ICON_CACHE = ProviderConfig.IS_DOGFOOD_BUILD && 23 Utilities.isPropertyEnabled(LogConfig.MEMORY_ONLY_ICON_CACHE); 24 25 private final String mTableName; 26 private final MySQLiteOpenHelper mOpenHelper; 27 28 private boolean mIgnoreWrites; 29 30 public SQLiteCacheHelper(Context context, String name, int version, String tableName) { 31 if (NO_ICON_CACHE) { 32 name = null; 33 } 34 mTableName = tableName; 35 mOpenHelper = new MySQLiteOpenHelper(context, name, version); 36 37 mIgnoreWrites = false; 38 } 39 40 /** 41 * @see SQLiteDatabase#delete(String, String, String[]) 42 */ 43 public void delete(String whereClause, String[] whereArgs) { 44 if (mIgnoreWrites) { 45 return; 46 } 47 try { 48 mOpenHelper.getWritableDatabase().delete(mTableName, whereClause, whereArgs); 49 } catch (SQLiteFullException e) { 50 onDiskFull(e); 51 } catch (SQLiteException e) { 52 Log.d(TAG, "Ignoring sqlite exception", e); 53 } 54 } 55 56 /** 57 * @see SQLiteDatabase#insertWithOnConflict(String, String, ContentValues, int) 58 */ 59 public void insertOrReplace(ContentValues values) { 60 if (mIgnoreWrites) { 61 return; 62 } 63 try { 64 mOpenHelper.getWritableDatabase().insertWithOnConflict( 65 mTableName, null, values, SQLiteDatabase.CONFLICT_REPLACE); 66 } catch (SQLiteFullException e) { 67 onDiskFull(e); 68 } catch (SQLiteException e) { 69 Log.d(TAG, "Ignoring sqlite exception", e); 70 } 71 } 72 73 private void onDiskFull(SQLiteFullException e) { 74 Log.e(TAG, "Disk full, all write operations will be ignored", e); 75 mIgnoreWrites = true; 76 } 77 78 /** 79 * @see SQLiteDatabase#query(String, String[], String, String[], String, String, String) 80 */ 81 public Cursor query(String[] columns, String selection, String[] selectionArgs) { 82 return mOpenHelper.getReadableDatabase().query( 83 mTableName, columns, selection, selectionArgs, null, null, null); 84 } 85 86 public void clear() { 87 mOpenHelper.clearDB(mOpenHelper.getWritableDatabase()); 88 } 89 90 protected abstract void onCreateTable(SQLiteDatabase db); 91 92 /** 93 * A private inner class to prevent direct DB access. 94 */ 95 private class MySQLiteOpenHelper extends SQLiteOpenHelper { 96 97 public MySQLiteOpenHelper(Context context, String name, int version) { 98 super(new NoLocaleSqliteContext(context), name, null, version); 99 } 100 101 @Override 102 public void onCreate(SQLiteDatabase db) { 103 onCreateTable(db); 104 } 105 106 @Override 107 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 108 if (oldVersion != newVersion) { 109 clearDB(db); 110 } 111 } 112 113 @Override 114 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 115 if (oldVersion != newVersion) { 116 clearDB(db); 117 } 118 } 119 120 private void clearDB(SQLiteDatabase db) { 121 db.execSQL("DROP TABLE IF EXISTS " + mTableName); 122 onCreate(db); 123 } 124 } 125} 126