diff options
Diffstat (limited to 'src/com/android/launcher3/LauncherProvider.java')
-rw-r--r-- | src/com/android/launcher3/LauncherProvider.java | 258 |
1 files changed, 90 insertions, 168 deletions
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 3150d5b0e..c84a4312d 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -53,17 +53,19 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.LauncherSettings.WorkspaceScreens; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.config.ProviderConfig; import com.android.launcher3.dynamicui.ExtractionUtils; import com.android.launcher3.graphics.IconShapeOverride; import com.android.launcher3.logging.FileLog; +import com.android.launcher3.model.DbDowngradeHelper; import com.android.launcher3.provider.LauncherDbUtils; +import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.util.ManagedProfileHeuristic; import com.android.launcher3.util.NoLocaleSqliteContext; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Thunk; +import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.reflect.Method; @@ -71,25 +73,20 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; public class LauncherProvider extends ContentProvider { private static final String TAG = "LauncherProvider"; private static final boolean LOGD = false; + private static final String DOWNGRADE_SCHEMA_FILE = "downgrade_schema.json"; + /** * Represents the schema of the database. Changes in scheme need not be backwards compatible. */ - private static final int SCHEMA_VERSION = 27; - /** - * Represents the actual data. It could include additional validations and normalizations added - * overtime. These must be backwards compatible, else we risk breaking old devices during - * restore or binary version downgrade. - */ - private static final int DATA_VERSION = 3; - - private static final String PREF_KEY_DATA_VERISON = "provider_data_version"; + public static final int SCHEMA_VERSION = 27; - public static final String AUTHORITY = ProviderConfig.AUTHORITY; + public static final String AUTHORITY = (BuildConfig.APPLICATION_ID + ".settings").intern(); static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; @@ -114,7 +111,7 @@ public class LauncherProvider extends ContentProvider { @Override public boolean onCreate() { - if (ProviderConfig.IS_DOGFOOD_BUILD) { + if (FeatureFlags.IS_DOGFOOD_BUILD) { Log.d(TAG, "Launcher process started"); } mListenerHandler = new Handler(mListenerWrapper); @@ -305,8 +302,7 @@ public class LauncherProvider extends ContentProvider { SqlArguments args = new SqlArguments(uri); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { + try (SQLiteTransaction t = new SQLiteTransaction(db)) { int numValues = values.length; for (int i = 0; i < numValues; i++) { addModifiedTime(values[i]); @@ -314,9 +310,7 @@ public class LauncherProvider extends ContentProvider { return 0; } } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); + t.commit(); } notifyListeners(); @@ -328,15 +322,11 @@ public class LauncherProvider extends ContentProvider { public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { createDbIfNotExists(); - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { + try (SQLiteTransaction t = new SQLiteTransaction(mOpenHelper.getWritableDatabase())) { ContentProviderResult[] result = super.applyBatch(operations); - db.setTransactionSuccessful(); + t.commit(); reloadLauncherIfExternal(); return result; - } finally { - db.endTransaction(); } } @@ -442,31 +432,26 @@ public class LauncherProvider extends ContentProvider { private ArrayList<Long> deleteEmptyFolders() { ArrayList<Long> folderIds = new ArrayList<>(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { + try (SQLiteTransaction t = new SQLiteTransaction(db)) { // Select folders whose id do not match any container value. String selection = LauncherSettings.Favorites.ITEM_TYPE + " = " + LauncherSettings.Favorites.ITEM_TYPE_FOLDER + " AND " + LauncherSettings.Favorites._ID + " NOT IN (SELECT " + LauncherSettings.Favorites.CONTAINER + " FROM " + Favorites.TABLE_NAME + ")"; - Cursor c = db.query(Favorites.TABLE_NAME, + try (Cursor c = db.query(Favorites.TABLE_NAME, new String[] {LauncherSettings.Favorites._ID}, - selection, null, null, null, null); - while (c.moveToNext()) { - folderIds.add(c.getLong(0)); + selection, null, null, null, null)) { + LauncherDbUtils.iterateCursor(c, 0, folderIds); } - c.close(); if (!folderIds.isEmpty()) { db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery( LauncherSettings.Favorites._ID, folderIds), null); } - db.setTransactionSuccessful(); + t.commit(); } catch (SQLException ex) { Log.e(TAG, ex.getMessage(), ex); folderIds.clear(); - } finally { - db.endTransaction(); } return folderIds; } @@ -714,50 +699,30 @@ public class LauncherProvider extends ContentProvider { @Override public void onOpen(SQLiteDatabase db) { super.onOpen(db); - SharedPreferences prefs = mContext - .getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, 0); - int oldVersion = prefs.getInt(PREF_KEY_DATA_VERISON, 0); - if (oldVersion != DATA_VERSION) { - // Only run the data upgrade path for an existing db. - if (!Utilities.getPrefs(mContext).getBoolean(EMPTY_DATABASE_CREATED, false)) { - db.beginTransaction(); - try { - onDataUpgrade(db, oldVersion); - db.setTransactionSuccessful(); - } catch (Exception e) { - Log.d(TAG, "Error updating data version, ignoring", e); - return; - } finally { - db.endTransaction(); - } - } - prefs.edit().putInt(PREF_KEY_DATA_VERISON, DATA_VERSION).apply(); + + File schemaFile = mContext.getFileStreamPath(DOWNGRADE_SCHEMA_FILE); + if (!schemaFile.exists()) { + handleOneTimeDataUpgrade(db); } + DbDowngradeHelper.updateSchemaFile(schemaFile, SCHEMA_VERSION, mContext, + R.raw.downgrade_schema); } /** - * Called when the data is updated as part of app update. It can be called multiple times - * with old version, even though it had been run before. The changes made here must be - * backwards compatible, else we risk breaking old devices during restore or binary - * version downgrade. + * One-time data updated before support of onDowngrade was added. This update is backwards + * compatible and can safely be run multiple times. + * Note: No new logic should be added here after release, as the new logic might not get + * executed on an existing device. + * TODO: Move this to db upgrade path, once the downgrade path is released. */ - protected void onDataUpgrade(SQLiteDatabase db, int oldVersion) { - switch (oldVersion) { - case 0: - case 1: { - // Remove "profile extra" - UserManagerCompat um = UserManagerCompat.getInstance(mContext); - for (UserHandle user : um.getUserProfiles()) { - long serial = um.getSerialNumberForUser(user); - String sql = "update favorites set intent = replace(intent, " - + "';l.profile=" + serial + ";', ';') where itemType = 0;"; - db.execSQL(sql); - } - } - case 2: - case 3: - // data updated - return; + protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { + // Remove "profile extra" + UserManagerCompat um = UserManagerCompat.getInstance(mContext); + for (UserHandle user : um.getUserProfiles()) { + long serial = um.getSerialNumberForUser(user); + String sql = "update favorites set intent = replace(intent, " + + "';l.profile=" + serial + ";', ';') where itemType = 0;"; + db.execSQL(sql); } } @@ -774,35 +739,29 @@ public class LauncherProvider extends ContentProvider { addWorkspacesTable(db, false); } case 13: { - db.beginTransaction(); - try { + try (SQLiteTransaction t = new SQLiteTransaction(db)) { // Insert new column for holding widget provider name db.execSQL("ALTER TABLE favorites " + "ADD COLUMN appWidgetProvider TEXT;"); - db.setTransactionSuccessful(); + t.commit(); } catch (SQLException ex) { Log.e(TAG, ex.getMessage(), ex); // Old version remains, which means we wipe old data break; - } finally { - db.endTransaction(); } } case 14: { - db.beginTransaction(); - try { + try (SQLiteTransaction t = new SQLiteTransaction(db)) { // Insert new column for holding update timestamp db.execSQL("ALTER TABLE favorites " + "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); db.execSQL("ALTER TABLE workspaceScreens " + "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); - db.setTransactionSuccessful(); + t.commit(); } catch (SQLException ex) { Log.e(TAG, ex.getMessage(), ex); // Old version remains, which means we wipe old data break; - } finally { - db.endTransaction(); } } case 15: { @@ -870,29 +829,25 @@ public class LauncherProvider extends ContentProvider { @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion == 28 && newVersion == 27) { - // TODO: remove this check. This is only applicable for internal development/testing - // and for any released version of Launcher. - return; + try { + DbDowngradeHelper.parse(mContext.getFileStreamPath(DOWNGRADE_SCHEMA_FILE)) + .onDowngrade(db, oldVersion, newVersion); + } catch (Exception e) { + Log.d(TAG, "Unable to downgrade from: " + oldVersion + " to " + newVersion + + ". Wiping databse.", e); + createEmptyDB(db); } - // This shouldn't happen -- throw our hands up in the air and start over. - Log.w(TAG, "Database version downgrade from: " + oldVersion + " to " + newVersion + - ". Wiping databse."); - createEmptyDB(db); } /** * Clears all the data for a fresh start. */ public void createEmptyDB(SQLiteDatabase db) { - db.beginTransaction(); - try { + try (SQLiteTransaction t = new SQLiteTransaction(db)) { db.execSQL("DROP TABLE IF EXISTS " + Favorites.TABLE_NAME); db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME); onCreate(db); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); + t.commit(); } } @@ -912,28 +867,26 @@ public class LauncherProvider extends ContentProvider { Log.e(TAG, "getAppWidgetIds not supported", e); return; } - try { - Cursor c = db.query(Favorites.TABLE_NAME, - new String[] {Favorites.APPWIDGET_ID }, - "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null, null, null); - HashSet<Integer> validWidgets = new HashSet<>(); + final HashSet<Integer> validWidgets = new HashSet<>(); + try (Cursor c = db.query(Favorites.TABLE_NAME, + new String[] {Favorites.APPWIDGET_ID }, + "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null, null, null)) { while (c.moveToNext()) { validWidgets.add(c.getInt(0)); } - c.close(); - - for (int widgetId : allWidgets) { - if (!validWidgets.contains(widgetId)) { - try { - FileLog.d(TAG, "Deleting invalid widget " + widgetId); - host.deleteAppWidgetId(widgetId); - } catch (RuntimeException e) { - // Ignore - } - } - } } catch (SQLException ex) { Log.w(TAG, "Error getting widgets list", ex); + return; + } + for (int widgetId : allWidgets) { + if (!validWidgets.contains(widgetId)) { + try { + FileLog.d(TAG, "Deleting invalid widget " + widgetId); + host.deleteAppWidgetId(widgetId); + } catch (RuntimeException e) { + // Ignore + } + } } } @@ -942,22 +895,16 @@ public class LauncherProvider extends ContentProvider { * launcher activity target with {@link Favorites#ITEM_TYPE_APPLICATION}. */ @Thunk void convertShortcutsToLauncherActivities(SQLiteDatabase db) { - db.beginTransaction(); - Cursor c = null; - SQLiteStatement updateStmt = null; - - try { - // Only consider the primary user as other users can't have a shortcut. - long userSerial = getDefaultUserSerial(); - c = db.query(Favorites.TABLE_NAME, new String[] { - Favorites._ID, - Favorites.INTENT, - }, "itemType=" + Favorites.ITEM_TYPE_SHORTCUT + " AND profileId=" + userSerial, - null, null, null, null); - - updateStmt = db.compileStatement("UPDATE favorites SET itemType=" - + Favorites.ITEM_TYPE_APPLICATION + " WHERE _id=?"); - + try (SQLiteTransaction t = new SQLiteTransaction(db); + // Only consider the primary user as other users can't have a shortcut. + Cursor c = db.query(Favorites.TABLE_NAME, + new String[] { Favorites._ID, Favorites.INTENT}, + "itemType=" + Favorites.ITEM_TYPE_SHORTCUT + + " AND profileId=" + getDefaultUserSerial(), + null, null, null, null); + SQLiteStatement updateStmt = db.compileStatement("UPDATE favorites SET itemType=" + + Favorites.ITEM_TYPE_APPLICATION + " WHERE _id=?") + ) { final int idIndex = c.getColumnIndexOrThrow(Favorites._ID); final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT); @@ -979,17 +926,9 @@ public class LauncherProvider extends ContentProvider { updateStmt.bindLong(1, id); updateStmt.executeUpdateDelete(); } - db.setTransactionSuccessful(); + t.commit(); } catch (SQLException ex) { Log.w(TAG, "Error deduping shortcuts", ex); - } finally { - db.endTransaction(); - if (c != null) { - c.close(); - } - if (updateStmt != null) { - updateStmt.close(); - } } } @@ -997,26 +936,17 @@ public class LauncherProvider extends ContentProvider { * Recreates workspace table and migrates data to the new table. */ public boolean recreateWorkspaceTable(SQLiteDatabase db) { - db.beginTransaction(); - try { - Cursor c = db.query(WorkspaceScreens.TABLE_NAME, + try (SQLiteTransaction t = new SQLiteTransaction(db)) { + final ArrayList<Long> sortedIDs; + + try (Cursor c = db.query(WorkspaceScreens.TABLE_NAME, new String[] {LauncherSettings.WorkspaceScreens._ID}, null, null, null, null, - LauncherSettings.WorkspaceScreens.SCREEN_RANK); - ArrayList<Long> sortedIDs = new ArrayList<Long>(); - long maxId = 0; - try { - while (c.moveToNext()) { - Long id = c.getLong(0); - if (!sortedIDs.contains(id)) { - sortedIDs.add(id); - maxId = Math.max(maxId, id); - } - } - } finally { - c.close(); + LauncherSettings.WorkspaceScreens.SCREEN_RANK)) { + // Use LinkedHashSet so that ordering is preserved + sortedIDs = new ArrayList<>( + LauncherDbUtils.iterateCursor(c, 0, new LinkedHashSet<Long>())); } - db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME); addWorkspacesTable(db, false); @@ -1029,21 +959,18 @@ public class LauncherProvider extends ContentProvider { addModifiedTime(values); db.insertOrThrow(WorkspaceScreens.TABLE_NAME, null, values); } - db.setTransactionSuccessful(); - mMaxScreenId = maxId; + t.commit(); + mMaxScreenId = sortedIDs.isEmpty() ? 0 : Collections.max(sortedIDs); } catch (SQLException ex) { // Old version remains, which means we wipe old data Log.e(TAG, ex.getMessage(), ex); return false; - } finally { - db.endTransaction(); } return true; } @Thunk boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) { - db.beginTransaction(); - try { + try (SQLiteTransaction t = new SQLiteTransaction(db)) { if (addRankColumn) { // Insert new column for holding rank db.execSQL("ALTER TABLE favorites ADD COLUMN rank INTEGER NOT NULL DEFAULT 0;"); @@ -1062,13 +989,11 @@ public class LauncherProvider extends ContentProvider { } c.close(); - db.setTransactionSuccessful(); + t.commit(); } catch (SQLException ex) { // Old version remains, which means we wipe old data Log.e(TAG, ex.getMessage(), ex); return false; - } finally { - db.endTransaction(); } return true; } @@ -1078,16 +1003,13 @@ public class LauncherProvider extends ContentProvider { } private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) { - db.beginTransaction(); - try { + try (SQLiteTransaction t = new SQLiteTransaction(db)) { db.execSQL("ALTER TABLE favorites ADD COLUMN " + columnName + " INTEGER NOT NULL DEFAULT " + defaultValue + ";"); - db.setTransactionSuccessful(); + t.commit(); } catch (SQLException ex) { Log.e(TAG, ex.getMessage(), ex); return false; - } finally { - db.endTransaction(); } return true; } |