diff options
author | Xin Li <delphij@google.com> | 2024-01-18 13:56:04 -0800 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2024-01-18 13:56:04 -0800 |
commit | 6810c1964607b9e75893c6af2efe82df87625653 (patch) | |
tree | 8acf8209b0a1a7899151b491f5894ad57bb88411 /src/com/android/launcher3/provider/RestoreDbTask.java | |
parent | 507cccdb174d25c7d3b5f802741e50041f6e5929 (diff) | |
parent | b7a5eb9ef418c9dce6f597244d2fb5b9b491610d (diff) | |
download | Launcher3-6810c1964607b9e75893c6af2efe82df87625653.tar.gz |
Merge Android 24Q1 Release (ab/11220357)
Bug: 319669529
Merged-In: I0708bf3c060ba84089722d0bd9480a4f4bd2b8e2
Change-Id: I31bb7f6aa8f71244f6e44903927f67e9d3a85642
Diffstat (limited to 'src/com/android/launcher3/provider/RestoreDbTask.java')
-rw-r--r-- | src/com/android/launcher3/provider/RestoreDbTask.java | 243 |
1 files changed, 187 insertions, 56 deletions
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index 4725dd16b2..dc8cd3af65 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -28,6 +28,8 @@ import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_I import android.app.backup.BackupManager; import android.appwidget.AppWidgetHost; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; import android.content.ContentValues; import android.content.Context; import android.content.Intent; @@ -42,20 +44,26 @@ import android.util.SparseLongArray; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import androidx.annotation.WorkerThread; -import com.android.launcher3.AppWidgetsRestoredReceiver; import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherPrefs; +import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Utilities; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.DeviceGridState; +import com.android.launcher3.model.LoaderTask; import com.android.launcher3.model.ModelDbController; +import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.pm.UserCache; import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; import com.android.launcher3.uioverrides.ApiWrapper; +import com.android.launcher3.util.ContentWriter; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.LogConfig; @@ -81,9 +89,9 @@ public class RestoreDbTask { public static final String APPWIDGET_OLD_IDS = "appwidget_old_ids"; public static final String APPWIDGET_IDS = "appwidget_ids"; - private static final String[] DB_COLUMNS_TO_LOG = {"profileId", "title", "itemType", "screen", - "container", "cellX", "cellY", "spanX", "spanY", "intent"}; + "container", "cellX", "cellY", "spanX", "spanY", "intent", "appWidgetProvider", + "appWidgetId", "restored"}; /** * Tries to restore the backup DB if needed @@ -133,16 +141,17 @@ public class RestoreDbTask { * 4. If restored from a single display backup, remove gaps between screenIds * 5. Override shortcuts that need to be replaced. * - * @return number of items deleted. + * @return number of items deleted */ @VisibleForTesting protected int sanitizeDB(Context context, ModelDbController controller, SQLiteDatabase db, BackupManager backupManager) throws Exception { - FileLog.d(TAG, "Old Launcher Database before sanitizing:"); + logFavoritesTable(db, "Old Launcher Database before sanitizing:", null, null); // Primary user ids long myProfileId = controller.getSerialNumberForUser(myUserHandle()); long oldProfileId = getDefaultProfileId(db); - FileLog.d(TAG, "sanitizeDB: myProfileId=" + myProfileId + " oldProfileId=" + oldProfileId); + FileLog.d(TAG, "sanitizeDB: myProfileId= " + myProfileId + + ", oldProfileId= " + oldProfileId); LongSparseArray<Long> oldManagedProfileIds = getManagedProfileIds(db, oldProfileId); LongSparseArray<Long> profileMapping = new LongSparseArray<>(oldManagedProfileIds.size() + 1); @@ -174,7 +183,7 @@ public class RestoreDbTask { final String[] args = new String[profileIds.length]; Arrays.fill(args, "?"); final String where = "profileId NOT IN (" + TextUtils.join(", ", Arrays.asList(args)) + ")"; - logUnrestoredItems(db, where, profileIds); + logFavoritesTable(db, "items to delete from unrestored profiles:", where, profileIds); int itemsDeletedCount = db.delete(Favorites.TABLE_NAME, where, profileIds); FileLog.d(TAG, itemsDeletedCount + " total items from unrestored user(s) were deleted"); @@ -234,47 +243,6 @@ public class RestoreDbTask { } /** - * Queries and logs the items we will delete from unrestored profiles in the launcher db. - * This is to understand why items might be missing during the restore process for Launcher. - * @param database the Launcher db to query from. - * @param where the SELECT statement to query items that will be deleted. - * @param profileIds the profile ID's the user will be migrating to. - */ - private void logUnrestoredItems(SQLiteDatabase database, String where, String[] profileIds) { - try (Cursor itemsToDelete = database.query( - /* table */ Favorites.TABLE_NAME, - /* columns */ DB_COLUMNS_TO_LOG, - /* selection */ where, - /* selection args */ profileIds, - /* groupBy */ null, - /* having */ null, - /* orderBy */ null - )) { - if (itemsToDelete.moveToFirst()) { - String[] columnNames = itemsToDelete.getColumnNames(); - StringBuilder stringBuilder = new StringBuilder( - "items to be deleted from the Favorites Table during restore:\n" - ); - do { - for (String columnName : columnNames) { - stringBuilder.append(columnName) - .append("=") - .append(itemsToDelete.getString( - itemsToDelete.getColumnIndex(columnName))) - .append(" "); - } - stringBuilder.append("\n"); - } while (itemsToDelete.moveToNext()); - FileLog.d(TAG, stringBuilder.toString()); - } else { - FileLog.d(TAG, "logDeletedItems: No items found to delete"); - } - } catch (Exception e) { - FileLog.e(TAG, "logDeletedItems: Error reading from database", e); - } - } - - /** * Remove gaps between screenIds to make sure no empty pages are left in between. * * e.g. [0, 3, 4, 6, 7] -> [0, 1, 2, 3, 4] @@ -377,26 +345,148 @@ public class RestoreDbTask { .putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType())); } - private void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller) { + @WorkerThread + @VisibleForTesting + void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller) { LauncherPrefs lp = LauncherPrefs.get(context); if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) { AppWidgetHost host = new AppWidgetHost(context, APPWIDGET_HOST_ID); - AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, controller, + restoreAppWidgetIds(context, controller, IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(), IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(), host); } else { - FileLog.d(TAG, "No app widget ids to restore."); + FileLog.d(TAG, "No app widget ids were received from backup to restore."); } lp.remove(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS); } - public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds, - @NonNull int[] newIds) { - LauncherPrefs.get(context).putSync( - OLD_APP_WIDGET_IDS.to(IntArray.wrap(oldIds).toConcatString()), - APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString())); + /** + * Updates the app widgets whose id has changed during the restore process. + */ + @WorkerThread + private void restoreAppWidgetIds(Context context, ModelDbController controller, + int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { + FileLog.e(TAG, "Skipping widget ID remap as widgets not supported"); + host.deleteHost(); + return; + } + if (!RestoreDbTask.isPending(context)) { + // Someone has already gone through our DB once, probably LoaderTask. Skip any further + // modifications of the DB. + FileLog.e(TAG, "Skipping widget ID remap as DB already in use"); + for (int widgetId : newWidgetIds) { + FileLog.d(TAG, "Deleting widgetId: " + widgetId); + host.deleteAppWidgetId(widgetId); + } + return; + } + + final AppWidgetManager widgets = AppWidgetManager.getInstance(context); + + FileLog.d(TAG, "restoreAppWidgetIds: " + + "oldWidgetIds=" + IntArray.wrap(oldWidgetIds).toConcatString() + + ", newWidgetIds=" + IntArray.wrap(newWidgetIds).toConcatString()); + + // TODO(b/234700507): Remove the logs after the bug is fixed + logDatabaseWidgetInfo(controller); + + for (int i = 0; i < oldWidgetIds.length; i++) { + FileLog.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]); + + final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]); + final int state; + if (LoaderTask.isValidProvider(provider)) { + // This will ensure that we show 'Click to setup' UI if required. + state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY; + } else { + state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; + } + + // b/135926478: Work profile widget restore is broken in platform. This forces us to + // recreate the widget during loading with the correct host provider. + long mainProfileId = UserCache.INSTANCE.get(context) + .getSerialNumberForUser(myUserHandle()); + long controllerProfileId = controller.getSerialNumberForUser(myUserHandle()); + String oldWidgetId = Integer.toString(oldWidgetIds[i]); + final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?"; + String profileId = Long.toString(mainProfileId); + final String[] args = new String[] { oldWidgetId, profileId }; + FileLog.d(TAG, "restoreAppWidgetIds: querying profile id=" + profileId + + " with controller profile ID=" + controllerProfileId); + int result = new ContentWriter(context, + new ContentWriter.CommitParams(controller, where, args)) + .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i]) + .put(LauncherSettings.Favorites.RESTORED, state) + .commit(); + if (result == 0) { + // TODO(b/234700507): Remove the logs after the bug is fixed + FileLog.e(TAG, "restoreAppWidgetIds: remapping failed since the widget is not in" + + " the database anymore"); + try (Cursor cursor = controller.getDb().query( + Favorites.TABLE_NAME, + new String[]{Favorites.APPWIDGET_ID}, + "appWidgetId=?", new String[]{oldWidgetId}, null, null, null)) { + if (!cursor.moveToFirst()) { + // The widget no long exists. + FileLog.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: " + + oldWidgetId); + host.deleteAppWidgetId(newWidgetIds[i]); + } + } + } + } + + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + if (app != null) { + app.getModel().forceReload(); + } + } + + private static void logDatabaseWidgetInfo(ModelDbController controller) { + try (Cursor cursor = controller.getDb().query(Favorites.TABLE_NAME, + new String[]{Favorites.APPWIDGET_ID, Favorites.RESTORED, Favorites.PROFILE_ID}, + Favorites.APPWIDGET_ID + "!=" + LauncherAppWidgetInfo.NO_ID, null, + null, null, null)) { + IntArray widgetIdList = new IntArray(); + IntArray widgetRestoreList = new IntArray(); + IntArray widgetProfileIdList = new IntArray(); + + if (cursor.moveToFirst()) { + final int widgetIdColumnIndex = cursor.getColumnIndex(Favorites.APPWIDGET_ID); + final int widgetRestoredColumnIndex = cursor.getColumnIndex(Favorites.RESTORED); + final int widgetProfileIdIndex = cursor.getColumnIndex(Favorites.PROFILE_ID); + while (!cursor.isAfterLast()) { + int widgetId = cursor.getInt(widgetIdColumnIndex); + int widgetRestoredFlag = cursor.getInt(widgetRestoredColumnIndex); + int widgetProfileId = cursor.getInt(widgetProfileIdIndex); + + widgetIdList.add(widgetId); + widgetRestoreList.add(widgetRestoredFlag); + widgetProfileIdList.add(widgetProfileId); + cursor.moveToNext(); + } + } + + StringBuilder builder = new StringBuilder(); + builder.append("["); + for (int i = 0; i < widgetIdList.size(); i++) { + builder.append("[") + .append(widgetIdList.get(i)) + .append(", ") + .append(widgetRestoreList.get(i)) + .append(", ") + .append(widgetProfileIdList.get(i)) + .append("]"); + } + builder.append("]"); + Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: " + + builder); + } catch (Exception ex) { + Log.e(TAG, "Getting widget ids from the database failed", ex); + } } protected static void maybeOverrideShortcuts(Context context, ModelDbController controller, @@ -442,4 +532,45 @@ public class RestoreDbTask { Collectors.joining(" OR ")); } + /** + * Queries and logs the items from the Favorites table in the launcher db. + * This is to understand why items might be missing during the restore process for Launcher. + * @param database The Launcher db to query from. + * @param logHeader First line in log statement, used to explain what is being logged. + * @param where The SELECT statement to query items. + * @param profileIds The profile ID's for each user profile. + */ + public static void logFavoritesTable(SQLiteDatabase database, @NonNull String logHeader, + String where, String[] profileIds) { + try (Cursor itemsToDelete = database.query( + /* table */ Favorites.TABLE_NAME, + /* columns */ DB_COLUMNS_TO_LOG, + /* selection */ where, + /* selection args */ profileIds, + /* groupBy */ null, + /* having */ null, + /* orderBy */ null + )) { + if (itemsToDelete.moveToFirst()) { + String[] columnNames = itemsToDelete.getColumnNames(); + StringBuilder stringBuilder = new StringBuilder(logHeader + "\n"); + do { + for (String columnName : columnNames) { + stringBuilder.append(columnName) + .append("=") + .append(itemsToDelete.getString( + itemsToDelete.getColumnIndex(columnName))) + .append(" "); + } + stringBuilder.append("\n"); + } while (itemsToDelete.moveToNext()); + FileLog.d(TAG, stringBuilder.toString()); + } else { + FileLog.d(TAG, "logFavoritesTable: No items found from query for" + + "\"" + logHeader + "\""); + } + } catch (Exception e) { + FileLog.e(TAG, "logFavoritesTable: Error reading from database", e); + } + } } |