diff options
| author | 2022-11-17 16:29:00 +0000 | |
|---|---|---|
| committer | 2022-11-17 16:29:00 +0000 | |
| commit | a5ca8d2495a2de2f12d0cb6d7947d1210e0ecaac (patch) | |
| tree | dc1e9bf12385d4be838a83613af6a7e7cd4356a6 | |
| parent | 6cdb9351e596f52c8570894971fe45c7a6f47e07 (diff) | |
| parent | c7076a7148b6aa5e75d29e57c2ffdd830e272747 (diff) | |
Merge "Integrate BackupAgent with B&REventLogger"
6 files changed, 213 insertions, 13 deletions
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl index 37c5cabc2376..811118479ef8 100644 --- a/core/java/android/app/IBackupAgent.aidl +++ b/core/java/android/app/IBackupAgent.aidl @@ -16,9 +16,12 @@ package android.app; +import android.app.backup.BackupRestoreEventLogger; import android.app.backup.IBackupCallback; import android.app.backup.IBackupManager; import android.os.ParcelFileDescriptor; + +import com.android.internal.infra.AndroidFuture; /** * Interface presented by applications being asked to participate in the @@ -193,4 +196,14 @@ oneway interface IBackupAgent { * @param message The message to be passed to the agent's application in an exception. */ void fail(String message); + + /** + * Provides the logging results that were accumulated in the BackupAgent during a backup or + * restore operation. This method should be called after the agent completes its backup or + * restore. + * + * @param resultsFuture a future that is completed with the logging results. + */ + void getLoggerResults( + in AndroidFuture<List<BackupRestoreEventLogger.DataTypeResult>> resultsFuture); } diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index b1b59b0e39b1..a4f612d7faee 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -41,6 +41,7 @@ import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.infra.AndroidFuture; import libcore.io.IoUtils; @@ -202,6 +203,7 @@ public abstract class BackupAgent extends ContextWrapper { Handler mHandler = null; + @Nullable private volatile BackupRestoreEventLogger mLogger = null; @Nullable private UserHandle mUser; // This field is written from the main thread (in onCreate), and read in a Binder thread (in // onFullBackup that is called from system_server via Binder). @@ -234,6 +236,20 @@ public abstract class BackupAgent extends ContextWrapper { } catch (InterruptedException e) { /* ignored */ } } + /** + * Get a logger to record app-specific backup and restore events that are happening during a + * backup or restore operation. + * + * <p>The logger instance had been created by the system with the correct {@link + * BackupRestoreEventLogger.OperationType} that corresponds to the operation the {@code + * BackupAgent} is currently handling. + * + * @hide + */ + @Nullable + public BackupRestoreEventLogger getBackupRestoreEventLogger() { + return mLogger; + } public BackupAgent() { super(null); @@ -264,6 +280,9 @@ public abstract class BackupAgent extends ContextWrapper { * @hide */ public void onCreate(UserHandle user, @OperationType int operationType) { + // TODO: Instantiate with the correct type using a parameter. + mLogger = new BackupRestoreEventLogger(BackupRestoreEventLogger.OperationType.BACKUP); + onCreate(); mUser = user; @@ -1305,6 +1324,16 @@ public abstract class BackupAgent extends ContextWrapper { } } } + + @Override + public void getLoggerResults( + AndroidFuture<List<BackupRestoreEventLogger.DataTypeResult>> in) { + if (mLogger != null) { + in.complete(mLogger.getLoggingResults()); + } else { + in.complete(Collections.emptyList()); + } + } } static class FailRunnable implements Runnable { diff --git a/core/java/android/app/backup/BackupRestoreEventLogger.aidl b/core/java/android/app/backup/BackupRestoreEventLogger.aidl new file mode 100644 index 000000000000..d6ef4e64258d --- /dev/null +++ b/core/java/android/app/backup/BackupRestoreEventLogger.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 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; + +parcelable BackupRestoreEventLogger.DataTypeResult;
\ No newline at end of file diff --git a/core/java/android/app/backup/BackupRestoreEventLogger.java b/core/java/android/app/backup/BackupRestoreEventLogger.java index 6f62c8a03078..68740cb3c086 100644 --- a/core/java/android/app/backup/BackupRestoreEventLogger.java +++ b/core/java/android/app/backup/BackupRestoreEventLogger.java @@ -19,6 +19,10 @@ package android.app.backup; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; import android.util.Slog; import java.lang.annotation.Retention; @@ -312,7 +316,7 @@ public class BackupRestoreEventLogger { /** * Encapsulate logging results for a single data type. */ - public static class DataTypeResult { + public static class DataTypeResult implements Parcelable { @BackupRestoreDataType private final String mDataType; private int mSuccessCount; @@ -362,5 +366,57 @@ public class BackupRestoreEventLogger { public byte[] getMetadataHash() { return mMetadataHash; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mDataType); + + dest.writeInt(mSuccessCount); + + dest.writeInt(mFailCount); + + Bundle errorsBundle = new Bundle(); + for (Map.Entry<String, Integer> e : mErrors.entrySet()) { + errorsBundle.putInt(e.getKey(), e.getValue()); + } + dest.writeBundle(errorsBundle); + + dest.writeByteArray(mMetadataHash); + } + + public static final Parcelable.Creator<DataTypeResult> CREATOR = + new Parcelable.Creator<>() { + public DataTypeResult createFromParcel(Parcel in) { + String dataType = in.readString(); + + int successCount = in.readInt(); + + int failCount = in.readInt(); + + Map<String, Integer> errors = new ArrayMap<>(); + Bundle errorsBundle = in.readBundle(getClass().getClassLoader()); + for (String key : errorsBundle.keySet()) { + errors.put(key, errorsBundle.getInt(key)); + } + + byte[] metadataHash = in.createByteArray(); + + DataTypeResult result = new DataTypeResult(dataType); + result.mSuccessCount = successCount; + result.mFailCount = failCount; + result.mErrors.putAll(errors); + result.mMetadataHash = metadataHash; + return result; + } + + public DataTypeResult[] newArray(int size) { + return new DataTypeResult[size]; + } + }; } } diff --git a/core/tests/coretests/src/android/app/backup/BackupAgentTest.java b/core/tests/coretests/src/android/app/backup/BackupAgentTest.java index 37cf4700c1d0..4d5b0d2d4ae7 100644 --- a/core/tests/coretests/src/android/app/backup/BackupAgentTest.java +++ b/core/tests/coretests/src/android/app/backup/BackupAgentTest.java @@ -46,6 +46,7 @@ import java.util.Set; public class BackupAgentTest { // An arbitrary user. private static final UserHandle USER_HANDLE = new UserHandle(15); + private static final String DATA_TYPE_BACKED_UP = "test data type"; @Mock FullBackup.BackupScheme mBackupScheme; @@ -73,6 +74,42 @@ public class BackupAgentTest { assertThat(rules).isEqualTo(expectedRules); } + @Test + public void getBackupRestoreEventLogger_beforeOnCreate_isNull() { + BackupAgent agent = new TestFullBackupAgent(); + + assertThat(agent.getBackupRestoreEventLogger()).isNull(); + } + + @Test + public void getBackupRestoreEventLogger_afterOnCreateForBackup_initializedForBackup() { + BackupAgent agent = new TestFullBackupAgent(); + agent.onCreate(USER_HANDLE, OperationType.BACKUP); // TODO: pass in new operation type + + assertThat(agent.getBackupRestoreEventLogger().getOperationType()).isEqualTo(1); + } + + @Test + public void getBackupRestoreEventLogger_afterOnCreateForRestore_initializedForRestore() { + BackupAgent agent = new TestFullBackupAgent(); + agent.onCreate(USER_HANDLE, OperationType.BACKUP); // TODO: pass in new operation type + + assertThat(agent.getBackupRestoreEventLogger().getOperationType()).isEqualTo(1); + } + + @Test + public void getBackupRestoreEventLogger_afterBackup_containsLogsLoggedByAgent() + throws Exception { + BackupAgent agent = new TestFullBackupAgent(); + agent.onCreate(USER_HANDLE, OperationType.BACKUP); // TODO: pass in new operation type + + // TestFullBackupAgent logs DATA_TYPE_BACKED_UP when onFullBackup is called. + agent.onFullBackup(new FullBackupDataOutput(/* quota = */ 0)); + + assertThat(agent.getBackupRestoreEventLogger().getLoggingResults().get(0).getDataType()) + .isEqualTo(DATA_TYPE_BACKED_UP); + } + private BackupAgent getAgentForOperationType(@OperationType int operationType) { BackupAgent agent = new TestFullBackupAgent(); agent.onCreate(USER_HANDLE, operationType); @@ -88,6 +125,11 @@ public class BackupAgentTest { } @Override + public void onFullBackup(FullBackupDataOutput data) { + getBackupRestoreEventLogger().logItemsBackedUp(DATA_TYPE_BACKED_UP, 1); + } + + @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // Left empty as this is a full backup agent. diff --git a/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java b/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java index bbd2ef38d786..b9fdc6d2aa23 100644 --- a/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java +++ b/core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java @@ -25,6 +25,7 @@ import static junit.framework.Assert.fail; import android.app.backup.BackupRestoreEventLogger.BackupRestoreDataType; import android.app.backup.BackupRestoreEventLogger.DataTypeResult; +import android.os.Parcel; import android.platform.test.annotations.Presubmit; import androidx.test.runner.AndroidJUnit4; @@ -35,6 +36,7 @@ import org.junit.runner.RunWith; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -236,10 +238,10 @@ public class BackupRestoreEventLoggerTest { mLogger.logItemsBackupFailed(DATA_TYPE_1, firstCount, ERROR_1); mLogger.logItemsBackupFailed(DATA_TYPE_1, secondCount, ERROR_2); - int firstErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1) - .getErrors().get(ERROR_1); - int secondErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1) - .getErrors().get(ERROR_2); + int firstErrorTypeCount = + getResultForDataType(mLogger, DATA_TYPE_1).getErrors().get(ERROR_1); + int secondErrorTypeCount = + getResultForDataType(mLogger, DATA_TYPE_1).getErrors().get(ERROR_2); assertThat(firstErrorTypeCount).isEqualTo(firstCount); assertThat(secondErrorTypeCount).isEqualTo(secondCount); } @@ -253,16 +255,54 @@ public class BackupRestoreEventLoggerTest { mLogger.logItemsRestoreFailed(DATA_TYPE_1, firstCount, ERROR_1); mLogger.logItemsRestoreFailed(DATA_TYPE_1, secondCount, ERROR_2); - int firstErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1) - .getErrors().get(ERROR_1); - int secondErrorTypeCount = getResultForDataType(mLogger, DATA_TYPE_1) - .getErrors().get(ERROR_2); + int firstErrorTypeCount = + getResultForDataType(mLogger, DATA_TYPE_1).getErrors().get(ERROR_1); + int secondErrorTypeCount = + getResultForDataType(mLogger, DATA_TYPE_1).getErrors().get(ERROR_2); assertThat(firstErrorTypeCount).isEqualTo(firstCount); assertThat(secondErrorTypeCount).isEqualTo(secondCount); } - private static DataTypeResult getResultForDataType(BackupRestoreEventLogger logger, - @BackupRestoreDataType String dataType) { + @Test + public void testGetLoggingResults_resultsParceledAndUnparceled_recreatedCorrectly() { + mLogger = new BackupRestoreEventLogger(RESTORE); + int firstTypeSuccessCount = 1; + int firstTypeErrorOneCount = 2; + int firstTypeErrorTwoCount = 3; + mLogger.logItemsRestored(DATA_TYPE_1, firstTypeSuccessCount); + mLogger.logItemsRestoreFailed(DATA_TYPE_1, firstTypeErrorOneCount, ERROR_1); + mLogger.logItemsRestoreFailed(DATA_TYPE_1, firstTypeErrorTwoCount, ERROR_2); + mLogger.logRestoreMetadata(DATA_TYPE_1, METADATA_1); + int secondTypeSuccessCount = 4; + int secondTypeErrorOneCount = 5; + mLogger.logItemsRestored(DATA_TYPE_2, secondTypeSuccessCount); + mLogger.logItemsRestoreFailed(DATA_TYPE_2, secondTypeErrorOneCount, ERROR_1); + + List<DataTypeResult> resultsList = mLogger.getLoggingResults(); + Parcel parcel = Parcel.obtain(); + + parcel.writeParcelableList(resultsList, /* flags= */ 0); + + parcel.setDataPosition(0); + List<DataTypeResult> recreatedList = new ArrayList<>(); + parcel.readParcelableList( + recreatedList, DataTypeResult.class.getClassLoader(), DataTypeResult.class); + + assertThat(recreatedList.get(0).getDataType()).isEqualTo(DATA_TYPE_1); + assertThat(recreatedList.get(0).getSuccessCount()).isEqualTo(firstTypeSuccessCount); + assertThat(recreatedList.get(0).getFailCount()) + .isEqualTo(firstTypeErrorOneCount + firstTypeErrorTwoCount); + assertThat(recreatedList.get(0).getErrors().get(ERROR_1)).isEqualTo(firstTypeErrorOneCount); + assertThat(recreatedList.get(0).getErrors().get(ERROR_2)).isEqualTo(firstTypeErrorTwoCount); + assertThat(recreatedList.get(1).getDataType()).isEqualTo(DATA_TYPE_2); + assertThat(recreatedList.get(1).getSuccessCount()).isEqualTo(secondTypeSuccessCount); + assertThat(recreatedList.get(1).getFailCount()).isEqualTo(secondTypeErrorOneCount); + assertThat(recreatedList.get(1).getErrors().get(ERROR_1)) + .isEqualTo(secondTypeErrorOneCount); + } + + private static DataTypeResult getResultForDataType( + BackupRestoreEventLogger logger, @BackupRestoreDataType String dataType) { Optional<DataTypeResult> result = getResultForDataTypeIfPresent(logger, dataType); if (result.isEmpty()) { fail("Failed to find result for data type: " + dataType); @@ -273,8 +313,9 @@ public class BackupRestoreEventLoggerTest { private static Optional<DataTypeResult> getResultForDataTypeIfPresent( BackupRestoreEventLogger logger, @BackupRestoreDataType String dataType) { List<DataTypeResult> resultList = logger.getLoggingResults(); - return resultList.stream().filter( - dataTypeResult -> dataTypeResult.getDataType().equals(dataType)).findAny(); + return resultList.stream() + .filter(dataTypeResult -> dataTypeResult.getDataType().equals(dataType)) + .findAny(); } private byte[] getMetaDataHash(String metaData) { |