diff options
| author | 2024-12-04 17:26:57 +0000 | |
|---|---|---|
| committer | 2024-12-05 08:49:05 +0000 | |
| commit | 53d643c9427b8a00a41d20d1e24d090d6944051d (patch) | |
| tree | dd43e778f317bf436cfc73c3df67bde9287e67cd | |
| parent | 9be5c49328ad2d438eca9db676410a44e643e0e4 (diff) | |
Add backup metrics for settings that use the extractRelevantValues() method.
Adds metrics logging for settings that are queried from the db. That is: system, global, secure and device_specific_config.
This is part of an effort to add metrics support to SettingsBackupAgent. For more details, see go/settings-backup-agent-metrics-design
Bug: 379861078
Change-Id: If8c881da91902df4d630bf8eba854556bbdf7d37
Flag: com.android.server.backup.enable_metrics_settings_backup_agents
Tested: Manually by verifying that the metrics are logged in GMS Core.
2 files changed, 211 insertions, 7 deletions
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 9ab853ff4964..e12c7a25e74c 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -22,6 +22,7 @@ import android.annotation.UserIdInt; import android.app.backup.BackupAgentHelper; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; +import android.app.backup.BackupRestoreEventLogger; import android.app.backup.FullBackupDataOutput; import android.content.ContentResolver; import android.content.ContentValues; @@ -82,6 +83,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.HashMap; import java.util.zip.CRC32; /** @@ -194,6 +196,12 @@ public class SettingsBackupAgent extends BackupAgentHelper { private static final String KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY = "pin_enhanced_privacy"; + // Error messages for logging metrics. + private static final String ERROR_COULD_NOT_READ_FROM_CURSOR = + "could_not_read_from_cursor"; + private static final String ERROR_FAILED_TO_WRITE_ENTITY = + "failed_to_write_entity"; + // Name of the temporary file we use during full backup/restore. This is // stored in the full-backup tarfile as well, so should not be changed. private static final String STAGE_FILE = "flattened-data"; @@ -224,6 +232,10 @@ public class SettingsBackupAgent extends BackupAgentHelper { // The font_scale default value for this device. private float mDefaultFontScale; + @Nullable private BackupRestoreEventLogger mBackupRestoreEventLogger; + @VisibleForTesting boolean areAgentMetricsEnabled = false; + @VisibleForTesting protected Map<String, Integer> numberOfSettingsPerKey; + @Override public void onCreate() { if (DEBUG_BACKUP) Log.d(TAG, "onCreate() invoked"); @@ -232,6 +244,11 @@ public class SettingsBackupAgent extends BackupAgentHelper { .getStringArray(R.array.entryvalues_font_size); mSettingsHelper = new SettingsHelper(this); mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); + if (com.android.server.backup.Flags.enableMetricsSettingsBackupAgents()) { + mBackupRestoreEventLogger = this.getBackupRestoreEventLogger(); + numberOfSettingsPerKey = new HashMap<>(); + areAgentMetricsEnabled = true; + } super.onCreate(); } @@ -654,23 +671,41 @@ public class SettingsBackupAgent extends BackupAgentHelper { if (oldChecksum == newChecksum) { return oldChecksum; } + writeDataForKey(key, data, output); + return newChecksum; + } + + @VisibleForTesting + void writeDataForKey(String key, byte[] data, BackupDataOutput output) { + boolean shouldLogMetrics = + areAgentMetricsEnabled && numberOfSettingsPerKey.containsKey(key); try { if (DEBUG_BACKUP) { Log.v(TAG, "Writing entity " + key + " of size " + data.length); } output.writeEntityHeader(key, data.length); output.writeEntityData(data, data.length); + if (shouldLogMetrics) { + mBackupRestoreEventLogger + .logItemsBackedUp(key, numberOfSettingsPerKey.get(key)); + } } catch (IOException ioe) { // Bail + if (shouldLogMetrics) { + mBackupRestoreEventLogger + .logItemsBackupFailed( + key, + numberOfSettingsPerKey.get(key), + ERROR_FAILED_TO_WRITE_ENTITY); + } } - return newChecksum; } private byte[] getSystemSettings() { Cursor cursor = getContentResolver().query(Settings.System.CONTENT_URI, PROJECTION, null, null, null); try { - return extractRelevantValues(cursor, SystemSettings.SETTINGS_TO_BACKUP); + return extractRelevantValues(cursor, SystemSettings.SETTINGS_TO_BACKUP, KEY_SYSTEM); } finally { cursor.close(); } @@ -680,7 +715,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { Cursor cursor = getContentResolver().query(Settings.Secure.CONTENT_URI, PROJECTION, null, null, null); try { - return extractRelevantValues(cursor, SecureSettings.SETTINGS_TO_BACKUP); + return extractRelevantValues(cursor, SecureSettings.SETTINGS_TO_BACKUP, KEY_SECURE); } finally { cursor.close(); } @@ -690,7 +725,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null, null, null); try { - return extractRelevantValues(cursor, getGlobalSettingsToBackup()); + return extractRelevantValues(cursor, getGlobalSettingsToBackup(), KEY_GLOBAL); } finally { cursor.close(); } @@ -1118,11 +1153,20 @@ public class SettingsBackupAgent extends BackupAgentHelper { * * @param cursor A cursor with settings data. * @param settings The settings to extract. + * @param settingsKey The key of the settings to extract (eg system). * @return The byte array of extracted values. */ - private byte[] extractRelevantValues(Cursor cursor, String[] settings) { + private byte[] extractRelevantValues( + Cursor cursor, String[] settings, String settingsKey) { if (!cursor.moveToFirst()) { Log.e(TAG, "Couldn't read from the cursor"); + if (areAgentMetricsEnabled) { + mBackupRestoreEventLogger + .logItemsBackupFailed( + settingsKey, + settings.length, + ERROR_COULD_NOT_READ_FROM_CURSOR); + } return new byte[0]; } @@ -1181,6 +1225,10 @@ public class SettingsBackupAgent extends BackupAgentHelper { } } + if (areAgentMetricsEnabled) { + numberOfSettingsPerKey.put(settingsKey, backedUpSettingIndex); + } + // Aggregate the result. byte[] result = new byte[totalSize]; int pos = 0; @@ -1364,7 +1412,9 @@ public class SettingsBackupAgent extends BackupAgentHelper { getContentResolver() .query(Settings.Secure.CONTENT_URI, PROJECTION, null, null, null)) { return extractRelevantValues( - cursor, DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP); + cursor, + DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP, + KEY_DEVICE_SPECIFIC_CONFIG); } } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java index 3a39150523ac..4642864612d3 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java @@ -17,11 +17,22 @@ package com.android.providers.settings; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertArrayEquals; - +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import android.app.backup.BackupAnnotations.BackupDestination; +import android.app.backup.BackupAnnotations.OperationType; +import android.app.backup.BackupDataOutput; +import android.app.backup.BackupRestoreEventLogger; +import android.app.backup.BackupRestoreEventLogger.DataTypeResult; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; @@ -32,7 +43,10 @@ import android.database.MatrixCursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.provider.settings.validators.SettingsValidators; import android.provider.settings.validators.Validator; @@ -44,8 +58,12 @@ import androidx.test.runner.AndroidJUnit4; import com.android.window.flags.Flags; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -54,12 +72,14 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; + /** * Tests for the SettingsHelperTest * Usage: atest SettingsProviderTest:SettingsBackupAgentTest @@ -73,6 +93,8 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { private static final Map<String, String> DEVICE_SPECIFIC_TEST_VALUES = new HashMap<>(); private static final Map<String, String> TEST_VALUES = new HashMap<>(); private static final Map<String, Validator> TEST_VALUES_VALIDATORS = new HashMap<>(); + private static final String TEST_KEY = "test_key"; + private static final String TEST_VALUE = "test_value"; static { DEVICE_SPECIFIC_TEST_VALUES.put(Settings.Secure.DISPLAY_DENSITY_FORCED, @@ -86,6 +108,13 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { TEST_VALUES_VALIDATORS.put(PRESERVED_TEST_SETTING, SettingsValidators.ANY_STRING_VALIDATOR); } + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock private BackupDataOutput mBackupDataOutput; + private TestFriendlySettingsBackupAgent mAgentUnderTest; private Context mContext; @@ -262,6 +291,110 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { assertEquals("1.5", testedMethod.apply("1.8")); } + @Test + @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void onCreate_metricsFlagIsDisabled_areAgentMetricsEnabledIsFalse() { + mAgentUnderTest.onCreate(); + + assertFalse(mAgentUnderTest.areAgentMetricsEnabled); + } + + @Test + @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void onCreate_flagIsEnabled_areAgentMetricsEnabledIsTrue() { + mAgentUnderTest.onCreate(); + + assertTrue(mAgentUnderTest.areAgentMetricsEnabled); + } + + @Test + @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyContainsKey_dataWriteSucceeds_logsSuccessMetrics() + throws IOException { + when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0); + when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0); + mAgentUnderTest.onCreate( + UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP); + mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1); + + mAgentUnderTest.writeDataForKey( + TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput); + + DataTypeResult loggingResult = + getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest); + assertNotNull(loggingResult); + assertEquals(loggingResult.getSuccessCount(), 1); + } + + @Test + @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyContainsKey_writeEntityHeaderFails_logsFailureMetrics() + throws IOException { + when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenThrow(new IOException()); + when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0); + mAgentUnderTest.onCreate( + UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP); + mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1); + + mAgentUnderTest.writeDataForKey( + TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput); + + DataTypeResult loggingResult = + getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest); + assertNotNull(loggingResult); + assertEquals(loggingResult.getFailCount(), 1); + } + + @Test + @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyContainsKey_writeEntityDataFails_logsFailureMetrics() + throws IOException { + when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0); + when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenThrow(new IOException()); + mAgentUnderTest.onCreate( + UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP); + mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1); + + mAgentUnderTest.writeDataForKey( + TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput); + + DataTypeResult loggingResult = + getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest); + assertNotNull(loggingResult); + assertEquals(loggingResult.getFailCount(), 1); + } + + @Test + @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void writeDataForKey_metricsFlagIsDisabled_doesNotLogMetrics() + throws IOException { + when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0); + when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0); + mAgentUnderTest.onCreate( + UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP); + mAgentUnderTest.setNumberOfSettingsPerKey(TEST_KEY, 1); + + mAgentUnderTest.writeDataForKey( + TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput); + + assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest)); + } + + @Test + @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void writeDataForKey_metricsFlagIsEnabled_numberOfSettingsPerKeyDoesNotContainKey_doesNotLogMetrics() + throws IOException { + when(mBackupDataOutput.writeEntityHeader(anyString(), anyInt())).thenReturn(0); + when(mBackupDataOutput.writeEntityData(any(byte[].class), anyInt())).thenReturn(0); + mAgentUnderTest.onCreate( + UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP); + + mAgentUnderTest.writeDataForKey( + TEST_KEY, TEST_VALUE.getBytes(), mBackupDataOutput); + + assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest)); + } + private byte[] generateBackupData(Map<String, String> keyValueData) { int totalBytes = 0; for (String key : keyValueData.keySet()) { @@ -329,6 +462,21 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { } } + private DataTypeResult getLoggingResultForDatatype( + String dataType, SettingsBackupAgent agent) { + if (agent.getBackupRestoreEventLogger() == null) { + return null; + } + List<DataTypeResult> loggingResults = + agent.getBackupRestoreEventLogger().getLoggingResults(); + for (DataTypeResult result : loggingResults) { + if (result.getDataType().equals(dataType)) { + return result; + } + } + return null; + } + private byte[] generateSingleKeyTestBackupData(String key, String value) throws IOException { try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { os.write(SettingsBackupAgent.toByteArray(key)); @@ -376,6 +524,12 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { return mSettingsWhitelist; } + + void setNumberOfSettingsPerKey(String key, int numberOfSettings) { + if (numberOfSettingsPerKey != null) { + this.numberOfSettingsPerKey.put(key, numberOfSettings); + } + } } /** The TestSettingsHelper tracks which values have been backed up and/or restored. */ |