Loading core/java/android/app/backup/BackupRestoreEventLogger.java +17 −0 Original line number Diff line number Diff line Loading @@ -34,9 +34,11 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * Class to log B&R stats for each data type that is backed up and restored by the calling app. Loading Loading @@ -325,6 +327,21 @@ public final class BackupRestoreEventLogger { } } /** @hide */ public static String toString(DataTypeResult result) { Objects.requireNonNull(result, "result cannot be null"); StringBuilder string = new StringBuilder("type=").append(result.mDataType) .append(", successCount=").append(result.mSuccessCount) .append(", failCount=").append(result.mFailCount); if (!result.mErrors.isEmpty()) { string.append(", errors=").append(result.mErrors); } if (result.mMetadataHash != null) { string.append(", metadataHash=").append(Arrays.toString(result.mMetadataHash)); } return string.toString(); } /** * Encapsulate logging results for a single data type. */ Loading core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.fail; import static org.junit.Assert.assertThrows; import android.app.backup.BackupRestoreEventLogger.DataTypeResult; import android.os.Parcel; import android.platform.test.annotations.Presubmit; Loading @@ -32,6 +34,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.server.backup.Flags; import com.google.common.truth.Expect; import org.junit.Before; import org.junit.Rule; import org.junit.Test; Loading @@ -42,6 +46,7 @@ import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; @Presubmit Loading @@ -64,6 +69,9 @@ public class BackupRestoreEventLoggerTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Rule public final Expect expect = Expect.create(); @Before public void setUp() throws Exception { mHashDigest = MessageDigest.getInstance("SHA-256"); Loading Loading @@ -366,6 +374,32 @@ public class BackupRestoreEventLoggerTest { assertThat(mLogger.getLoggingResults()).isEmpty(); } @Test public void testDataTypeResultToString_nullArgs() { assertThrows(NullPointerException.class, () -> BackupRestoreEventLogger.toString(null)); } @Test public void testDataTypeResultToString_typeOnly() { DataTypeResult result = new DataTypeResult("The Type is Bond, James Bond!"); expect.withMessage("toString()") .that(BackupRestoreEventLogger.toString(result)).isEqualTo( "type=The Type is Bond, James Bond!, successCount=0, failCount=0"); } @Test public void testDataTypeResultToString_allFields() { DataTypeResult result = DataTypeResultTest.createDataTypeResult( "The Type is Bond, James Bond!", /* successCount= */ 42, /* failCount= */ 108, Map.of("D'OH!", 666, "", 0), new byte[] { 4, 8, 15, 16, 23, 42 }); expect.withMessage("toString()") .that(BackupRestoreEventLogger.toString(result)).isEqualTo( "type=The Type is Bond, James Bond!, successCount=42, failCount=108, " + "errors={=0, D'OH!=666}, metadataHash=[4, 8, 15, 16, 23, 42]"); } private static DataTypeResult getResultForDataType( BackupRestoreEventLogger logger, String dataType) { Optional<DataTypeResult> result = getResultForDataTypeIfPresent(logger, dataType); Loading core/tests/coretests/src/android/app/backup/DataTypeResultTest.java 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.backup; import static com.google.common.truth.Truth.assertWithMessage; import android.app.backup.BackupRestoreEventLogger.DataTypeResult; import android.os.Bundle; import android.os.Parcel; import com.google.common.truth.Expect; import org.junit.Rule; import org.junit.Test; import java.util.Map; public final class DataTypeResultTest { @Rule public final Expect expect = Expect.create(); @Test public void testGetters_defaultConstructorFields() { var result = new DataTypeResult("The Type is Bond, James Bond!"); expect.withMessage("getDataType()").that(result.getDataType()) .isEqualTo("The Type is Bond, James Bond!"); expect.withMessage("getSuccessCount()").that(result.getSuccessCount()).isEqualTo(0); expect.withMessage("getFailCount()").that(result.getFailCount()).isEqualTo(0); expect.withMessage("getErrorsCount()").that(result.getErrors()).isEmpty(); expect.withMessage("getMetadataHash()").that(result.getMetadataHash()).isNull(); expect.withMessage("describeContents()").that(result.describeContents()).isEqualTo(0); } @Test public void testGetters_allFields() { DataTypeResult result = createDataTypeResult("The Type is Bond, James Bond!", /* successCount= */ 42, /* failCount= */ 108, Map.of("D'OH!", 666), new byte[] { 4, 8, 15, 16, 23, 42 }); expect.withMessage("getDataType()").that(result.getDataType()) .isEqualTo("The Type is Bond, James Bond!"); expect.withMessage("getSuccessCount()").that(result.getSuccessCount()).isEqualTo(42); expect.withMessage("getFailCount()").that(result.getFailCount()).isEqualTo(108); expect.withMessage("getErrorsCount()").that(result.getErrors()).containsExactly("D'OH!", 666); expect.withMessage("getMetadataHash()").that(result.getMetadataHash()).asList() .containsExactly((byte) 4, (byte) 8, (byte) 15, (byte) 16, (byte) 23, (byte) 42) .inOrder(); expect.withMessage("describeContents()").that(result.describeContents()).isEqualTo(0); } @Test public void testParcelMethods() { DataTypeResult original = createDataTypeResult("The Type is Bond, James Bond!", /* successCount= */ 42, /* failCount= */ 108, Map.of("D'OH!", 666), new byte[] { 4, 8, 15, 16, 23, 42 }); Parcel parcel = Parcel.obtain(); try { original.writeToParcel(parcel, /* flags= */ 0); parcel.setDataPosition(0); var clone = DataTypeResult.CREATOR.createFromParcel(parcel); assertWithMessage("createFromParcel()").that(clone).isNotNull(); expect.withMessage("getDataType()").that(clone.getDataType()) .isEqualTo(original.getDataType()); expect.withMessage("getSuccessCount()").that(clone.getSuccessCount()) .isEqualTo(original.getSuccessCount()); expect.withMessage("getFailCount()").that(clone.getFailCount()) .isEqualTo(original.getFailCount()); expect.withMessage("getErrorsCount()").that(clone.getErrors()) .containsExactlyEntriesIn(original.getErrors()).inOrder(); expect.withMessage("getMetadataHash()").that(clone.getMetadataHash()) .isEqualTo(original.getMetadataHash()); expect.withMessage("describeContents()").that(clone.describeContents()).isEqualTo(0); } finally { parcel.recycle(); } } static DataTypeResult createDataTypeResult(String dataType, int successCount, int failCount, Map<String, Integer> errors, byte... metadataHash) { Parcel parcel = Parcel.obtain(); try { parcel.writeString(dataType); parcel.writeInt(successCount); parcel.writeInt(failCount); Bundle errorsBundle = new Bundle(); errors.entrySet() .forEach(entry -> errorsBundle.putInt(entry.getKey(), entry.getValue())); parcel.writeBundle(errorsBundle); parcel.writeByteArray(metadataHash); parcel.setDataPosition(0); var result = DataTypeResult.CREATOR.createFromParcel(parcel); assertWithMessage("createFromParcel()").that(result).isNotNull(); return result; } finally { parcel.recycle(); } } } packages/LocalTransport/src/com/android/localtransport/LocalTransport.java +3 −15 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.app.backup.BackupAnnotations; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupManagerMonitor; import android.app.backup.BackupRestoreEventLogger; import android.app.backup.BackupRestoreEventLogger.DataTypeResult; import android.app.backup.BackupTransport; import android.app.backup.RestoreDescription; Loading Loading @@ -52,7 +53,6 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; Loading Loading @@ -924,24 +924,12 @@ public class LocalTransport extends BackupTransport { BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS, DataTypeResult.class); for (DataTypeResult result : results) { Log.i(TAG, "\tdataType: " + result.getDataType()); Log.i(TAG, "\tsuccessCount: " + result.getSuccessCount()); Log.i(TAG, "\tfailCount: " + result.getFailCount()); Log.i(TAG, "\tmetadataHash: " + Arrays.toString(result.getMetadataHash())); if (!result.getErrors().isEmpty()) { Log.i(TAG, "\terrors {"); for (String error : result.getErrors().keySet()) { Log.i(TAG, "\t\t" + error + ": " + result.getErrors().get(error)); } Log.i(TAG, "\t}"); Log.i(TAG, "\t" + BackupRestoreEventLogger.toString(result)); } Log.i(TAG, "}"); } } } } @NonNull @Override Loading Loading
core/java/android/app/backup/BackupRestoreEventLogger.java +17 −0 Original line number Diff line number Diff line Loading @@ -34,9 +34,11 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * Class to log B&R stats for each data type that is backed up and restored by the calling app. Loading Loading @@ -325,6 +327,21 @@ public final class BackupRestoreEventLogger { } } /** @hide */ public static String toString(DataTypeResult result) { Objects.requireNonNull(result, "result cannot be null"); StringBuilder string = new StringBuilder("type=").append(result.mDataType) .append(", successCount=").append(result.mSuccessCount) .append(", failCount=").append(result.mFailCount); if (!result.mErrors.isEmpty()) { string.append(", errors=").append(result.mErrors); } if (result.mMetadataHash != null) { string.append(", metadataHash=").append(Arrays.toString(result.mMetadataHash)); } return string.toString(); } /** * Encapsulate logging results for a single data type. */ Loading
core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java +34 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.fail; import static org.junit.Assert.assertThrows; import android.app.backup.BackupRestoreEventLogger.DataTypeResult; import android.os.Parcel; import android.platform.test.annotations.Presubmit; Loading @@ -32,6 +34,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.server.backup.Flags; import com.google.common.truth.Expect; import org.junit.Before; import org.junit.Rule; import org.junit.Test; Loading @@ -42,6 +46,7 @@ import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; @Presubmit Loading @@ -64,6 +69,9 @@ public class BackupRestoreEventLoggerTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Rule public final Expect expect = Expect.create(); @Before public void setUp() throws Exception { mHashDigest = MessageDigest.getInstance("SHA-256"); Loading Loading @@ -366,6 +374,32 @@ public class BackupRestoreEventLoggerTest { assertThat(mLogger.getLoggingResults()).isEmpty(); } @Test public void testDataTypeResultToString_nullArgs() { assertThrows(NullPointerException.class, () -> BackupRestoreEventLogger.toString(null)); } @Test public void testDataTypeResultToString_typeOnly() { DataTypeResult result = new DataTypeResult("The Type is Bond, James Bond!"); expect.withMessage("toString()") .that(BackupRestoreEventLogger.toString(result)).isEqualTo( "type=The Type is Bond, James Bond!, successCount=0, failCount=0"); } @Test public void testDataTypeResultToString_allFields() { DataTypeResult result = DataTypeResultTest.createDataTypeResult( "The Type is Bond, James Bond!", /* successCount= */ 42, /* failCount= */ 108, Map.of("D'OH!", 666, "", 0), new byte[] { 4, 8, 15, 16, 23, 42 }); expect.withMessage("toString()") .that(BackupRestoreEventLogger.toString(result)).isEqualTo( "type=The Type is Bond, James Bond!, successCount=42, failCount=108, " + "errors={=0, D'OH!=666}, metadataHash=[4, 8, 15, 16, 23, 42]"); } private static DataTypeResult getResultForDataType( BackupRestoreEventLogger logger, String dataType) { Optional<DataTypeResult> result = getResultForDataTypeIfPresent(logger, dataType); Loading
core/tests/coretests/src/android/app/backup/DataTypeResultTest.java 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.backup; import static com.google.common.truth.Truth.assertWithMessage; import android.app.backup.BackupRestoreEventLogger.DataTypeResult; import android.os.Bundle; import android.os.Parcel; import com.google.common.truth.Expect; import org.junit.Rule; import org.junit.Test; import java.util.Map; public final class DataTypeResultTest { @Rule public final Expect expect = Expect.create(); @Test public void testGetters_defaultConstructorFields() { var result = new DataTypeResult("The Type is Bond, James Bond!"); expect.withMessage("getDataType()").that(result.getDataType()) .isEqualTo("The Type is Bond, James Bond!"); expect.withMessage("getSuccessCount()").that(result.getSuccessCount()).isEqualTo(0); expect.withMessage("getFailCount()").that(result.getFailCount()).isEqualTo(0); expect.withMessage("getErrorsCount()").that(result.getErrors()).isEmpty(); expect.withMessage("getMetadataHash()").that(result.getMetadataHash()).isNull(); expect.withMessage("describeContents()").that(result.describeContents()).isEqualTo(0); } @Test public void testGetters_allFields() { DataTypeResult result = createDataTypeResult("The Type is Bond, James Bond!", /* successCount= */ 42, /* failCount= */ 108, Map.of("D'OH!", 666), new byte[] { 4, 8, 15, 16, 23, 42 }); expect.withMessage("getDataType()").that(result.getDataType()) .isEqualTo("The Type is Bond, James Bond!"); expect.withMessage("getSuccessCount()").that(result.getSuccessCount()).isEqualTo(42); expect.withMessage("getFailCount()").that(result.getFailCount()).isEqualTo(108); expect.withMessage("getErrorsCount()").that(result.getErrors()).containsExactly("D'OH!", 666); expect.withMessage("getMetadataHash()").that(result.getMetadataHash()).asList() .containsExactly((byte) 4, (byte) 8, (byte) 15, (byte) 16, (byte) 23, (byte) 42) .inOrder(); expect.withMessage("describeContents()").that(result.describeContents()).isEqualTo(0); } @Test public void testParcelMethods() { DataTypeResult original = createDataTypeResult("The Type is Bond, James Bond!", /* successCount= */ 42, /* failCount= */ 108, Map.of("D'OH!", 666), new byte[] { 4, 8, 15, 16, 23, 42 }); Parcel parcel = Parcel.obtain(); try { original.writeToParcel(parcel, /* flags= */ 0); parcel.setDataPosition(0); var clone = DataTypeResult.CREATOR.createFromParcel(parcel); assertWithMessage("createFromParcel()").that(clone).isNotNull(); expect.withMessage("getDataType()").that(clone.getDataType()) .isEqualTo(original.getDataType()); expect.withMessage("getSuccessCount()").that(clone.getSuccessCount()) .isEqualTo(original.getSuccessCount()); expect.withMessage("getFailCount()").that(clone.getFailCount()) .isEqualTo(original.getFailCount()); expect.withMessage("getErrorsCount()").that(clone.getErrors()) .containsExactlyEntriesIn(original.getErrors()).inOrder(); expect.withMessage("getMetadataHash()").that(clone.getMetadataHash()) .isEqualTo(original.getMetadataHash()); expect.withMessage("describeContents()").that(clone.describeContents()).isEqualTo(0); } finally { parcel.recycle(); } } static DataTypeResult createDataTypeResult(String dataType, int successCount, int failCount, Map<String, Integer> errors, byte... metadataHash) { Parcel parcel = Parcel.obtain(); try { parcel.writeString(dataType); parcel.writeInt(successCount); parcel.writeInt(failCount); Bundle errorsBundle = new Bundle(); errors.entrySet() .forEach(entry -> errorsBundle.putInt(entry.getKey(), entry.getValue())); parcel.writeBundle(errorsBundle); parcel.writeByteArray(metadataHash); parcel.setDataPosition(0); var result = DataTypeResult.CREATOR.createFromParcel(parcel); assertWithMessage("createFromParcel()").that(result).isNotNull(); return result; } finally { parcel.recycle(); } } }
packages/LocalTransport/src/com/android/localtransport/LocalTransport.java +3 −15 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.app.backup.BackupAnnotations; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupManagerMonitor; import android.app.backup.BackupRestoreEventLogger; import android.app.backup.BackupRestoreEventLogger.DataTypeResult; import android.app.backup.BackupTransport; import android.app.backup.RestoreDescription; Loading Loading @@ -52,7 +53,6 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; Loading Loading @@ -924,24 +924,12 @@ public class LocalTransport extends BackupTransport { BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS, DataTypeResult.class); for (DataTypeResult result : results) { Log.i(TAG, "\tdataType: " + result.getDataType()); Log.i(TAG, "\tsuccessCount: " + result.getSuccessCount()); Log.i(TAG, "\tfailCount: " + result.getFailCount()); Log.i(TAG, "\tmetadataHash: " + Arrays.toString(result.getMetadataHash())); if (!result.getErrors().isEmpty()) { Log.i(TAG, "\terrors {"); for (String error : result.getErrors().keySet()) { Log.i(TAG, "\t\t" + error + ": " + result.getErrors().get(error)); } Log.i(TAG, "\t}"); Log.i(TAG, "\t" + BackupRestoreEventLogger.toString(result)); } Log.i(TAG, "}"); } } } } @NonNull @Override Loading