summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Sarp Misoglu <sarpm@google.com> 2022-11-17 16:29:00 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-11-17 16:29:00 +0000
commita5ca8d2495a2de2f12d0cb6d7947d1210e0ecaac (patch)
treedc1e9bf12385d4be838a83613af6a7e7cd4356a6
parent6cdb9351e596f52c8570894971fe45c7a6f47e07 (diff)
parentc7076a7148b6aa5e75d29e57c2ffdd830e272747 (diff)
Merge "Integrate BackupAgent with B&REventLogger"
-rw-r--r--core/java/android/app/IBackupAgent.aidl13
-rw-r--r--core/java/android/app/backup/BackupAgent.java29
-rw-r--r--core/java/android/app/backup/BackupRestoreEventLogger.aidl19
-rw-r--r--core/java/android/app/backup/BackupRestoreEventLogger.java58
-rw-r--r--core/tests/coretests/src/android/app/backup/BackupAgentTest.java42
-rw-r--r--core/tests/coretests/src/android/app/backup/BackupRestoreEventLoggerTest.java65
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) {