diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2015-12-18 22:32:36 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2015-12-18 22:32:36 +0000 |
commit | d3b53c3fa44f7e3a92e6971150626d7c59a178d5 (patch) | |
tree | 084d51aa68518961c850efbbbe600247ac782cfa | |
parent | aff1a1df8a09ff89f762c5d9915597219c022dae (diff) | |
parent | 60acb943c68e2f949fc8ad4703c9156549263897 (diff) | |
download | android_packages_apps_Trebuchet-d3b53c3fa44f7e3a92e6971150626d7c59a178d5.tar.gz android_packages_apps_Trebuchet-d3b53c3fa44f7e3a92e6971150626d7c59a178d5.tar.bz2 android_packages_apps_Trebuchet-d3b53c3fa44f7e3a92e6971150626d7c59a178d5.zip |
Merge "Preventing launcher crashes due to low disk space." into ub-launcher3-burnaby-polish
am: 60acb943c6
* commit '60acb943c68e2f949fc8ad4703c9156549263897':
Preventing launcher crashes due to low disk space.
-rw-r--r-- | src/com/android/launcher3/IconCache.java | 149 | ||||
-rw-r--r-- | src/com/android/launcher3/WidgetPreviewLoader.java | 68 | ||||
-rw-r--r-- | src/com/android/launcher3/util/SQLiteCacheHelper.java | 128 |
3 files changed, 217 insertions, 128 deletions
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index efb978d11..d39ae661e 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -28,7 +28,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteException; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; @@ -48,6 +48,7 @@ import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.SQLiteCacheHelper; import com.android.launcher3.util.Thunk; import java.util.Collections; @@ -231,7 +232,7 @@ public class IconCache { public synchronized void removeIconsForPkg(String packageName, UserHandleCompat user) { removeFromMemCacheLocked(packageName, user); long userSerial = mUserManager.getSerialNumberForUser(user); - mIconDb.getWritableDatabase().delete(IconDB.TABLE_NAME, + mIconDb.delete( IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?", new String[]{packageName + "/%", Long.toString(userSerial)}); } @@ -276,58 +277,65 @@ public class IconCache { componentMap.put(app.getComponentName(), app); } - Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME, - new String[] {IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, - IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION, - IconDB.COLUMN_SYSTEM_STATE}, - IconDB.COLUMN_USER + " = ? ", - new String[] {Long.toString(userSerial)}, - null, null, null); - - final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT); - final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED); - final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION); - final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID); - final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE); - HashSet<Integer> itemsToRemove = new HashSet<Integer>(); Stack<LauncherActivityInfoCompat> appsToUpdate = new Stack<>(); - while (c.moveToNext()) { - String cn = c.getString(indexComponent); - ComponentName component = ComponentName.unflattenFromString(cn); - PackageInfo info = pkgInfoMap.get(component.getPackageName()); - if (info == null) { - if (!ignorePackages.contains(component.getPackageName())) { + Cursor c = null; + try { + c = mIconDb.query( + new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, + IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION, + IconDB.COLUMN_SYSTEM_STATE}, + IconDB.COLUMN_USER + " = ? ", + new String[]{Long.toString(userSerial)}); + + final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT); + final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED); + final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION); + final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID); + final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE); + + while (c.moveToNext()) { + String cn = c.getString(indexComponent); + ComponentName component = ComponentName.unflattenFromString(cn); + PackageInfo info = pkgInfoMap.get(component.getPackageName()); + if (info == null) { + if (!ignorePackages.contains(component.getPackageName())) { + remove(component, user); + itemsToRemove.add(c.getInt(rowIndex)); + } + continue; + } + if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) { + // Application is not present + continue; + } + + long updateTime = c.getLong(indexLastUpdate); + int version = c.getInt(indexVersion); + LauncherActivityInfoCompat app = componentMap.remove(component); + if (version == info.versionCode && updateTime == info.lastUpdateTime && + TextUtils.equals(mSystemState, c.getString(systemStateIndex))) { + continue; + } + if (app == null) { remove(component, user); itemsToRemove.add(c.getInt(rowIndex)); + } else { + appsToUpdate.add(app); } - continue; - } - if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) { - // Application is not present - continue; } - - long updateTime = c.getLong(indexLastUpdate); - int version = c.getInt(indexVersion); - LauncherActivityInfoCompat app = componentMap.remove(component); - if (version == info.versionCode && updateTime == info.lastUpdateTime && - TextUtils.equals(mSystemState, c.getString(systemStateIndex))) { - continue; - } - if (app == null) { - remove(component, user); - itemsToRemove.add(c.getInt(rowIndex)); - } else { - appsToUpdate.add(app); + } catch (SQLiteException e) { + Log.d(TAG, "Error reading icon cache", e); + // Continue updating whatever we have read so far + } finally { + if (c != null) { + c.close(); } } - c.close(); if (!itemsToRemove.isEmpty()) { - mIconDb.getWritableDatabase().delete(IconDB.TABLE_NAME, - Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, itemsToRemove), - null); + mIconDb.delete( + Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, itemsToRemove), null); } // Insert remaining apps. @@ -357,8 +365,7 @@ public class IconCache { values.put(IconDB.COLUMN_USER, userSerial); values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime); values.put(IconDB.COLUMN_VERSION, info.versionCode); - mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values, - SQLiteDatabase.CONFLICT_REPLACE); + mIconDb.insertOrReplace(values); } @Thunk ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app, @@ -679,19 +686,18 @@ public class IconCache { ContentValues values = newContentValues(icon, lowResIcon, label); values.put(IconDB.COLUMN_COMPONENT, componentName.flattenToString()); values.put(IconDB.COLUMN_USER, userSerial); - mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values, - SQLiteDatabase.CONFLICT_REPLACE); + mIconDb.insertOrReplace(values); } private boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) { - Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME, - new String[] {lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON, + Cursor c = null; + try { + c = mIconDb.query( + new String[]{lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON, IconDB.COLUMN_LABEL}, IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", - new String[] {cacheKey.componentName.flattenToString(), - Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))}, - null, null, null); - try { + new String[]{cacheKey.componentName.flattenToString(), + Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))}); if (c.moveToNext()) { entry.icon = loadIconNoResize(c, 0, lowRes ? mLowResOptions : null); entry.isLowResIcon = lowRes; @@ -705,8 +711,12 @@ public class IconCache { } return true; } + } catch (SQLiteException e) { + Log.d(TAG, "Error reading icon cache", e); } finally { - c.close(); + if (c != null) { + c.close(); + } } return false; } @@ -752,9 +762,9 @@ public class IconCache { LauncherActivityInfoCompat app = mAppsToUpdate.pop(); String cn = app.getComponentName().flattenToString(); ContentValues values = updateCacheAndGetContentValues(app, true); - mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values, + mIconDb.update(values, IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", - new String[] {cn, Long.toString(mUserSerial)}); + new String[]{cn, Long.toString(mUserSerial)}); mUpdatedPackages.add(app.getComponentName().getPackageName()); if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) { @@ -789,7 +799,7 @@ public class IconCache { mSystemState = Locale.getDefault().toString(); } - private static final class IconDB extends SQLiteOpenHelper { + private static final class IconDB extends SQLiteCacheHelper { private final static int DB_VERSION = 7; private final static int RELEASE_VERSION = DB_VERSION + @@ -807,11 +817,11 @@ public class IconCache { private final static String COLUMN_SYSTEM_STATE = "system_state"; public IconDB(Context context) { - super(context, LauncherFiles.APP_ICONS_DB, null, RELEASE_VERSION); + super(context, LauncherFiles.APP_ICONS_DB, RELEASE_VERSION, TABLE_NAME); } @Override - public void onCreate(SQLiteDatabase db) { + protected void onCreateTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + COLUMN_COMPONENT + " TEXT NOT NULL, " + COLUMN_USER + " INTEGER NOT NULL, " + @@ -824,25 +834,6 @@ public class IconCache { "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " + ");"); } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion != newVersion) { - clearDB(db); - } - } - - @Override - public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion != newVersion) { - clearDB(db); - } - } - - private void clearDB(SQLiteDatabase db) { - db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); - onCreate(db); - } } private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, String label) { diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 10c105326..b27fa60b7 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -10,7 +10,6 @@ import android.content.res.Resources; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; @@ -32,6 +31,7 @@ import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.SQLiteCacheHelper; import com.android.launcher3.util.Thunk; import com.android.launcher3.widget.WidgetCell; @@ -104,7 +104,7 @@ public class WidgetPreviewLoader { * The DB holds the generated previews for various components. Previews can also have different * sizes (landscape vs portrait). */ - private static class CacheDb extends SQLiteOpenHelper { + private static class CacheDb extends SQLiteCacheHelper { private static final int DB_VERSION = 4; private static final String TABLE_NAME = "shortcut_and_widget_previews"; @@ -117,11 +117,11 @@ public class WidgetPreviewLoader { private static final String COLUMN_PREVIEW_BITMAP = "preview_bitmap"; public CacheDb(Context context) { - super(context, LauncherFiles.WIDGET_PREVIEWS_DB, null, DB_VERSION); + super(context, LauncherFiles.WIDGET_PREVIEWS_DB, DB_VERSION, TABLE_NAME); } @Override - public void onCreate(SQLiteDatabase database) { + public void onCreateTable(SQLiteDatabase database) { database.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + COLUMN_COMPONENT + " TEXT NOT NULL, " + COLUMN_USER + " INTEGER NOT NULL, " + @@ -133,25 +133,6 @@ public class WidgetPreviewLoader { "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ", " + COLUMN_SIZE + ") " + ");"); } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion != newVersion) { - clearDB(db); - } - } - - @Override - public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion != newVersion) { - clearDB(db); - } - } - - private void clearDB(SQLiteDatabase db) { - db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); - onCreate(db); - } } private WidgetCacheKey getObjectKey(Object o, String size) { @@ -176,13 +157,7 @@ public class WidgetPreviewLoader { values.put(CacheDb.COLUMN_VERSION, versions[0]); values.put(CacheDb.COLUMN_LAST_UPDATED, versions[1]); values.put(CacheDb.COLUMN_PREVIEW_BITMAP, Utilities.flattenBitmap(preview)); - - try { - mDb.getWritableDatabase().insertWithOnConflict(CacheDb.TABLE_NAME, null, values, - SQLiteDatabase.CONFLICT_REPLACE); - } catch (SQLException e) { - Log.e(TAG, "Error saving image to DB", e); - } + mDb.insertOrReplace(values); } public void removePackage(String packageName, UserHandleCompat user) { @@ -194,13 +169,9 @@ public class WidgetPreviewLoader { mPackageVersions.remove(packageName); } - try { - mDb.getWritableDatabase().delete(CacheDb.TABLE_NAME, - CacheDb.COLUMN_PACKAGE + " = ? AND " + CacheDb.COLUMN_USER + " = ?", - new String[] {packageName, Long.toString(userSerial)}); - } catch (SQLException e) { - Log.e(TAG, "Unable to delete items from DB", e); - } + mDb.delete( + CacheDb.COLUMN_PACKAGE + " = ? AND " + CacheDb.COLUMN_USER + " = ?", + new String[]{packageName, Long.toString(userSerial)}); } /** @@ -238,10 +209,10 @@ public class WidgetPreviewLoader { LongSparseArray<HashSet<String>> packagesToDelete = new LongSparseArray<>(); Cursor c = null; try { - c = mDb.getReadableDatabase().query(CacheDb.TABLE_NAME, - new String[] {CacheDb.COLUMN_USER, CacheDb.COLUMN_PACKAGE, - CacheDb.COLUMN_LAST_UPDATED, CacheDb.COLUMN_VERSION}, - null, null, null, null, null); + c = mDb.query( + new String[]{CacheDb.COLUMN_USER, CacheDb.COLUMN_PACKAGE, + CacheDb.COLUMN_LAST_UPDATED, CacheDb.COLUMN_VERSION}, + null, null); while (c.moveToNext()) { long userId = c.getLong(0); String pkg = c.getString(1); @@ -274,7 +245,7 @@ public class WidgetPreviewLoader { } } } catch (SQLException e) { - Log.e(TAG, "Error updatating widget previews", e); + Log.e(TAG, "Error updating widget previews", e); } finally { if (c != null) { c.close(); @@ -288,16 +259,15 @@ public class WidgetPreviewLoader { @Thunk Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle, PreviewLoadTask loadTask) { Cursor cursor = null; try { - cursor = mDb.getReadableDatabase().query( - CacheDb.TABLE_NAME, - new String[] { CacheDb.COLUMN_PREVIEW_BITMAP }, - CacheDb.COLUMN_COMPONENT + " = ? AND " + CacheDb.COLUMN_USER + " = ? AND " + CacheDb.COLUMN_SIZE + " = ?", - new String[] { + cursor = mDb.query( + new String[]{CacheDb.COLUMN_PREVIEW_BITMAP}, + CacheDb.COLUMN_COMPONENT + " = ? AND " + CacheDb.COLUMN_USER + " = ? AND " + + CacheDb.COLUMN_SIZE + " = ?", + new String[]{ key.componentName.flattenToString(), Long.toString(mUserManager.getSerialNumberForUser(key.user)), key.size - }, - null, null, null); + }); // If cancelled, skip getting the blob and decoding it into a bitmap if (loadTask.isCancelled()) { return null; diff --git a/src/com/android/launcher3/util/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java new file mode 100644 index 000000000..62a30d0d4 --- /dev/null +++ b/src/com/android/launcher3/util/SQLiteCacheHelper.java @@ -0,0 +1,128 @@ +package com.android.launcher3.util; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteFullException; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; + +/** + * An extension of {@link SQLiteOpenHelper} with utility methods for a single table cache DB. + * Any exception during write operations are ignored, and any version change causes a DB reset. + */ +public abstract class SQLiteCacheHelper { + private static final String TAG = "SQLiteCacheHelper"; + + private final String mTableName; + private final MySQLiteOpenHelper mOpenHelper; + + private boolean mIgnoreWrites; + + public SQLiteCacheHelper(Context context, String name, int version, String tableName) { + mTableName = tableName; + mOpenHelper = new MySQLiteOpenHelper(context, name, version); + + mIgnoreWrites = false; + } + + /** + * @see SQLiteDatabase#update(String, ContentValues, String, String[]) + */ + public void update(ContentValues values, String whereClause, String[] whereArgs) { + if (mIgnoreWrites) { + return; + } + try { + mOpenHelper.getWritableDatabase().update(mTableName, values, whereClause, whereArgs); + } catch (SQLiteFullException e) { + onDiskFull(e); + } catch (SQLiteException e) { + Log.d(TAG, "Ignoring sqlite exception", e); + } + } + + /** + * @see SQLiteDatabase#delete(String, String, String[]) + */ + public void delete(String whereClause, String[] whereArgs) { + if (mIgnoreWrites) { + return; + } + try { + mOpenHelper.getWritableDatabase().delete(mTableName, whereClause, whereArgs); + } catch (SQLiteFullException e) { + onDiskFull(e); + } catch (SQLiteException e) { + Log.d(TAG, "Ignoring sqlite exception", e); + } + } + + /** + * @see SQLiteDatabase#insertWithOnConflict(String, String, ContentValues, int) + */ + public void insertOrReplace(ContentValues values) { + if (mIgnoreWrites) { + return; + } + try { + mOpenHelper.getWritableDatabase().insertWithOnConflict( + mTableName, null, values, SQLiteDatabase.CONFLICT_REPLACE); + } catch (SQLiteFullException e) { + onDiskFull(e); + } catch (SQLiteException e) { + Log.d(TAG, "Ignoring sqlite exception", e); + } + } + + private void onDiskFull(SQLiteFullException e) { + Log.e(TAG, "Disk full, all write operations will be ignored", e); + mIgnoreWrites = true; + } + + /** + * @see SQLiteDatabase#query(String, String[], String, String[], String, String, String) + */ + public Cursor query(String[] columns, String selection, String[] selectionArgs) { + return mOpenHelper.getReadableDatabase().query( + mTableName, columns, selection, selectionArgs, null, null, null); + } + + protected abstract void onCreateTable(SQLiteDatabase db); + + /** + * A private inner class to prevent direct DB access. + */ + private class MySQLiteOpenHelper extends SQLiteOpenHelper { + + public MySQLiteOpenHelper(Context context, String name, int version) { + super(context, name, null, version); + } + + @Override + public void onCreate(SQLiteDatabase db) { + onCreateTable(db); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion != newVersion) { + clearDB(db); + } + } + + @Override + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion != newVersion) { + clearDB(db); + } + } + + private void clearDB(SQLiteDatabase db) { + db.execSQL("DROP TABLE IF EXISTS " + mTableName); + onCreate(db); + } + } +} |