summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Daniel Perez <perezibanez@google.com> 2024-12-04 17:26:57 +0000
committer Daniel Perez <perezibanez@google.com> 2024-12-05 08:49:05 +0000
commit53d643c9427b8a00a41d20d1e24d090d6944051d (patch)
treedd43e778f317bf436cfc73c3df67bde9287e67cd
parent9be5c49328ad2d438eca9db676410a44e643e0e4 (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.
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java62
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java156
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. */