Adds restore metrics to RestoreDbTask.java

Bug: 307527314
Flag: ACONFIG enable_launcher_br_metrics TEAMFOOD
Test: locally verified
Change-Id: I105b17276cf7e2c270c819854a8fbc269b7c81b4
This commit is contained in:
Charlie Anderson
2024-01-02 17:05:00 -05:00
parent 2c385bec7b
commit 1fcf8da4e5
3 changed files with 83 additions and 26 deletions
@@ -22,6 +22,8 @@ open class LauncherRestoreEventLogger : ResourceBasedOverride {
const val RESTORE_ERROR_SHORTCUT_NOT_FOUND = "shortcut_not_found"
const val RESTORE_ERROR_APP_NOT_INSTALLED = "app_not_installed"
const val RESTORE_ERROR_WIDGETS_DISABLED = "widgets_disabled"
const val RESTORE_ERROR_PROFILE_NOT_RESTORED = "profile_not_restored"
const val RESTORE_ERROR_WIDGET_REMOVED = "widget_not_found"
fun newInstance(context: Context?): LauncherRestoreEventLogger {
return ResourceBasedOverride.Overrides.getObject(
@@ -18,12 +18,18 @@ package com.android.launcher3.provider;
import static android.os.Process.myUserHandle;
import static com.android.launcher3.Flags.enableLauncherBrMetrics;
import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
import static com.android.launcher3.LauncherPrefs.RESTORE_DEVICE;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_PROFILE_NOT_RESTORED;
import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_WIDGETS_DISABLED;
import static com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RESTORE_ERROR_WIDGET_REMOVED;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_ID;
@@ -53,6 +59,7 @@ 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.backuprestore.LauncherRestoreEventLogger;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.DeviceGridState;
import com.android.launcher3.model.LoaderTask;
@@ -123,8 +130,11 @@ public class RestoreDbTask {
FileLog.d(TAG, "performRestore: starting restore from db");
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
RestoreDbTask task = new RestoreDbTask();
task.sanitizeDB(context, controller, db, new BackupManager(context));
task.restoreAppWidgetIdsIfExists(context, controller);
BackupManager backupManager = new BackupManager(context);
LauncherRestoreEventLogger restoreEventLogger =
LauncherRestoreEventLogger.Companion.newInstance(context);
task.sanitizeDB(context, controller, db, backupManager, restoreEventLogger);
task.restoreAppWidgetIdsIfExists(context, controller, restoreEventLogger);
t.commit();
return true;
} catch (Exception e) {
@@ -147,7 +157,8 @@ public class RestoreDbTask {
*/
@VisibleForTesting
protected int sanitizeDB(Context context, ModelDbController controller, SQLiteDatabase db,
BackupManager backupManager) throws Exception {
BackupManager backupManager, LauncherRestoreEventLogger restoreEventLogger)
throws Exception {
logFavoritesTable(db, "Old Launcher Database before sanitizing:", null, null);
// Primary user ids
long myProfileId = controller.getSerialNumberForUser(myUserHandle());
@@ -186,6 +197,9 @@ public class RestoreDbTask {
Arrays.fill(args, "?");
final String where = "profileId NOT IN (" + TextUtils.join(", ", Arrays.asList(args)) + ")";
logFavoritesTable(db, "items to delete from unrestored profiles:", where, profileIds);
if (enableLauncherBrMetrics()) {
reportUnrestoredProfiles(db, where, profileIds, restoreEventLogger);
}
int itemsDeletedCount = db.delete(Favorites.TABLE_NAME, where, profileIds);
FileLog.d(TAG, itemsDeletedCount + " total items from unrestored user(s) were deleted");
@@ -345,21 +359,24 @@ public class RestoreDbTask {
DeviceGridState deviceGridState = new DeviceGridState(context);
FileLog.d(TAG, "restore initiated from backup: DeviceGridState=" + deviceGridState);
LauncherPrefs.get(context).putSync(RESTORE_DEVICE.to(deviceGridState.getDeviceType()));
LauncherPrefs.get(context).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(true));
if (enableLauncherBrMetrics()) {
LauncherPrefs.get(context).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(true));
}
}
@WorkerThread
@VisibleForTesting
void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller) {
void restoreAppWidgetIdsIfExists(Context context, ModelDbController controller,
LauncherRestoreEventLogger restoreEventLogger) {
LauncherPrefs lp = LauncherPrefs.get(context);
if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) {
AppWidgetHost host = new AppWidgetHost(context, APPWIDGET_HOST_ID);
restoreAppWidgetIds(context, controller,
restoreAppWidgetIds(context, controller, restoreEventLogger,
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 were received from backup to restore.");
FileLog.d(TAG, "Did not receive new app widget id map during Launcher restore");
}
lp.remove(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS);
@@ -370,10 +387,13 @@ public class RestoreDbTask {
*/
@WorkerThread
private void restoreAppWidgetIds(Context context, ModelDbController controller,
int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) {
LauncherRestoreEventLogger launcherRestoreEventLogger, 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();
launcherRestoreEventLogger.logFavoritesItemsRestoreFailed(Favorites.ITEM_TYPE_APPWIDGET,
oldWidgetIds.length, RESTORE_ERROR_WIDGETS_DISABLED);
return;
}
if (!RestoreDbTask.isPending(context)) {
@@ -437,11 +457,16 @@ public class RestoreDbTask {
FileLog.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: "
+ oldWidgetId);
host.deleteAppWidgetId(newWidgetIds[i]);
launcherRestoreEventLogger.logSingleFavoritesItemRestoreFailed(
ITEM_TYPE_APPWIDGET,
RESTORE_ERROR_WIDGET_REMOVED
);
}
}
}
}
logFavoritesTable(controller.getDb(), "launcher db after remap widget ids", null, null);
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
if (app != null) {
app.getModel().forceReload();
@@ -476,17 +501,16 @@ public class RestoreDbTask {
StringBuilder builder = new StringBuilder();
builder.append("[");
for (int i = 0; i < widgetIdList.size(); i++) {
builder.append("[")
builder.append("[appWidgetId=")
.append(widgetIdList.get(i))
.append(", ")
.append(", restoreFlag=")
.append(widgetRestoreList.get(i))
.append(", ")
.append(", profileId=")
.append(widgetProfileIdList.get(i))
.append("]");
}
builder.append("]");
Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: "
+ builder);
Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: " + builder);
} catch (Exception ex) {
Log.e(TAG, "Getting widget ids from the database failed", ex);
}
@@ -545,7 +569,7 @@ public class RestoreDbTask {
*/
public static void logFavoritesTable(SQLiteDatabase database, @NonNull String logHeader,
String where, String[] profileIds) {
try (Cursor itemsToDelete = database.query(
try (Cursor cursor = database.query(
/* table */ Favorites.TABLE_NAME,
/* columns */ DB_COLUMNS_TO_LOG,
/* selection */ where,
@@ -554,26 +578,53 @@ public class RestoreDbTask {
/* having */ null,
/* orderBy */ null
)) {
if (itemsToDelete.moveToFirst()) {
String[] columnNames = itemsToDelete.getColumnNames();
if (cursor.moveToFirst()) {
String[] columnNames = cursor.getColumnNames();
StringBuilder stringBuilder = new StringBuilder(logHeader + "\n");
do {
for (String columnName : columnNames) {
stringBuilder.append(columnName)
.append("=")
.append(itemsToDelete.getString(
itemsToDelete.getColumnIndex(columnName)))
.append(cursor.getString(
cursor.getColumnIndex(columnName)))
.append(" ");
}
stringBuilder.append("\n");
} while (itemsToDelete.moveToNext());
} while (cursor.moveToNext());
FileLog.d(TAG, stringBuilder.toString());
} else {
FileLog.d(TAG, "logFavoritesTable: No items found from query for"
FileLog.d(TAG, "logFavoritesTable: No items found from query for "
+ "\"" + logHeader + "\"");
}
} catch (Exception e) {
FileLog.e(TAG, "logFavoritesTable: Error reading from database", e);
}
}
/**
* Queries and reports the count of each itemType to be removed due to unrestored profiles.
* @param database The Launcher db to query from.
* @param where Query being used for to find unrestored profiles
* @param profileIds profile ids that were not restored
* @param restoreEventLogger Backup/Restore Logger to report metrics
*/
private void reportUnrestoredProfiles(SQLiteDatabase database, String where,
String[] profileIds, LauncherRestoreEventLogger restoreEventLogger) {
final String query = "SELECT itemType, COUNT(*) AS count FROM favorites WHERE "
+ where + " GROUP BY itemType";
try (Cursor cursor = database.rawQuery(query, profileIds)) {
if (cursor.moveToFirst()) {
do {
restoreEventLogger.logFavoritesItemsRestoreFailed(
cursor.getInt(cursor.getColumnIndexOrThrow(ITEM_TYPE)),
cursor.getInt(cursor.getColumnIndexOrThrow("count")),
RESTORE_ERROR_PROFILE_NOT_RESTORED
);
} while (cursor.moveToNext());
}
} catch (Exception e) {
FileLog.e(TAG, "reportUnrestoredProfiles: Error reading from database", e);
}
}
}
@@ -59,6 +59,7 @@ 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.backuprestore.LauncherRestoreEventLogger;
import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LauncherModelHelper;
@@ -90,6 +91,7 @@ public class RestoreDbTaskTest {
private SQLiteDatabase mMockDb;
private Cursor mMockCursor;
private LauncherPrefs mPrefs;
private LauncherRestoreEventLogger mMockRestoreEventLogger;
@Before
public void setup() {
@@ -100,6 +102,7 @@ public class RestoreDbTaskTest {
mMockDb = mock(SQLiteDatabase.class);
mMockCursor = mock(Cursor.class);
mPrefs = new LauncherPrefs(mContext);
mMockRestoreEventLogger = mock(LauncherRestoreEventLogger.class);
}
@After
@@ -178,7 +181,7 @@ public class RestoreDbTaskTest {
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
mTask.sanitizeDB(mContext, controller, controller.getDb(), bm);
mTask.sanitizeDB(mContext, controller, controller.getDb(), bm, mMockRestoreEventLogger);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -207,7 +210,7 @@ public class RestoreDbTaskTest {
assertEquals(10, getItemCountForProfile(db, myProfileId_old));
assertEquals(6, getItemCountForProfile(db, workProfileId_old));
mTask.sanitizeDB(mContext, controller, controller.getDb(), bm);
mTask.sanitizeDB(mContext, controller, controller.getDb(), bm, mMockRestoreEventLogger);
// All the data has been migrated to the new user ids
assertEquals(0, getItemCountForProfile(db, myProfileId_old));
@@ -219,7 +222,7 @@ public class RestoreDbTaskTest {
@Test
public void givenLauncherPrefsHasNoIds_whenRestoreAppWidgetIdsIfExists_thenIdsAreRemoved() {
// When
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
}
@@ -235,7 +238,7 @@ public class RestoreDbTaskTest {
// When
setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds);
@@ -257,7 +260,7 @@ public class RestoreDbTaskTest {
// When
setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds);
@@ -280,12 +283,13 @@ public class RestoreDbTaskTest {
when(mMockDb.query(any(), any(), any(), any(), any(), any(), any()))
.thenReturn(mMockCursor);
when(mMockCursor.moveToFirst()).thenReturn(true);
when(mMockCursor.getColumnNames()).thenReturn(new String[] {});
when(mMockCursor.isAfterLast()).thenReturn(true);
RestoreDbTask.setPending(mContext);
// When
setRestoredAppWidgetIds(mContext, expectedOldIds, expectedNewIds);
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController);
mTask.restoreAppWidgetIdsIfExists(mContext, mMockController, mMockRestoreEventLogger);
// Then
assertThat(expectedHost.getAppWidgetIds()).isEqualTo(allExpectedIds);