Add APIs to log B&R events during delayed restore

Add methods to BackupManager to allow system apps to acquire
BackupRestoreEventLogger and upload logs after a restore operation for
their package had already finished.

Move UserBackupManagerServiceTest under 'mockingservicetests' target and
add testReportDelayedRestoreResult_sendsLogsToMonitor that mocks a
static method.

Bug: 260687705
Test: atest UserBackupManagerServiceTest
Change-Id: Ia873efe27847e0b2a489ef083a36fc001e6167c3
This commit is contained in:
Ruslan Tkhakokhov
2022-12-13 22:53:42 +00:00
parent ab86ac0249
commit 033b9645c8
9 changed files with 192 additions and 20 deletions

View File

@@ -1481,11 +1481,13 @@ package android.app.backup {
method @RequiresPermission(android.Manifest.permission.BACKUP) public android.content.Intent getDataManagementIntent(String);
method @Nullable @RequiresPermission(android.Manifest.permission.BACKUP) public CharSequence getDataManagementIntentLabel(@NonNull String);
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.BACKUP) public String getDataManagementLabel(@NonNull String);
method @NonNull public android.app.backup.BackupRestoreEventLogger getDelayedRestoreLogger();
method @RequiresPermission(android.Manifest.permission.BACKUP) public String getDestinationString(String);
method @RequiresPermission(android.Manifest.permission.BACKUP) public boolean isAppEligibleForBackup(String);
method @RequiresPermission(android.Manifest.permission.BACKUP) public boolean isBackupEnabled();
method @RequiresPermission(android.Manifest.permission.BACKUP) public boolean isBackupServiceActive(android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.BACKUP) public String[] listAllTransports();
method @NonNull public void reportDelayedRestoreResult(@NonNull android.app.backup.BackupRestoreEventLogger);
method @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[], android.app.backup.BackupObserver);
method @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[], android.app.backup.BackupObserver, android.app.backup.BackupManagerMonitor, int);
method @Deprecated public int requestRestore(android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor);

View File

@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.backup.BackupAnnotations.OperationType;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -1041,6 +1042,42 @@ public class BackupManager {
return backupAgent.getBackupRestoreEventLogger();
}
/**
* Get an instance of {@link BackupRestoreEventLogger} to report B&R related events during a
* delayed restore operation.
*
* @return an instance of {@link BackupRestoreEventLogger}.
*
* @hide
*/
@NonNull
@SystemApi
public BackupRestoreEventLogger getDelayedRestoreLogger() {
return new BackupRestoreEventLogger(OperationType.RESTORE);
}
/**
* Report B&R related events following a delayed restore operation.
*
* @param logger an instance of {@link BackupRestoreEventLogger} to which the corresponding
* events have been logged.
*
* @hide
*/
@NonNull
@SystemApi
public void reportDelayedRestoreResult(@NonNull BackupRestoreEventLogger logger) {
checkServiceBinder();
if (sService != null) {
try {
sService.reportDelayedRestoreResult(mContext.getPackageName(),
logger.getLoggingResults());
} catch (RemoteException e) {
Log.w(TAG, "reportDelayedRestoreResult() couldn't connect");
}
}
}
/*
* We wrap incoming binder calls with a private class implementation that
* redirects them into main-thread actions. This serializes the backup

View File

@@ -16,6 +16,7 @@
package android.app.backup;
import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.app.backup.IBackupObserver;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IFullBackupRestoreObserver;
@@ -722,4 +723,6 @@ interface IBackupManager {
* that have been excluded will be passed to the agent to make it aware of the exclusions.
*/
void excludeKeysFromRestore(String packageName, in List<String> keys);
void reportDelayedRestoreResult(in String packageName, in List<DataTypeResult> results);
}

View File

@@ -83,6 +83,13 @@ public class BackupManagerTest {
() -> mBackupManager.getBackupRestoreEventLogger(agent));
}
@Test
public void testGetDelayedRestoreLogger_returnsRestoreLogger() {
BackupRestoreEventLogger logger = mBackupManager.getDelayedRestoreLogger();
assertThat(logger.getOperationType()).isEqualTo(OperationType.RESTORE);
}
private static BackupAgent getTestAgent() {
return new BackupAgent() {
@Override

View File

@@ -25,6 +25,8 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -1556,6 +1558,22 @@ public class BackupManagerService extends IBackupManager.Stub {
}
}
public void reportDelayedRestoreResult(String packageName, List<DataTypeResult> results) {
int userId = Binder.getCallingUserHandle().getIdentifier();
if (!isUserReadyForBackup(userId)) {
Slog.w(TAG, "Returning from reportDelayedRestoreResult as backup for user" + userId +
" is not initialized yet");
return;
}
UserBackupManagerService userBackupManagerService =
getServiceForUserIfCallerHasPermission(userId,
/* caller */ "reportDelayedRestoreResult()");
if (userBackupManagerService != null) {
userBackupManagerService.reportDelayedRestoreResult(packageName, results);
}
}
/**
* Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
* If the user is not registered with the service (either the user is locked or not eligible for

View File

@@ -49,6 +49,7 @@ import android.app.backup.BackupAgent;
import android.app.backup.BackupAnnotations.BackupDestination;
import android.app.backup.BackupManager;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.FullBackup;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
@@ -505,13 +506,14 @@ public class UserBackupManagerService {
@VisibleForTesting
UserBackupManagerService(Context context, PackageManager packageManager,
LifecycleOperationStorage operationStorage) {
LifecycleOperationStorage operationStorage, TransportManager transportManager) {
mContext = context;
mUserId = 0;
mRegisterTransportsRequestedTime = 0;
mPackageManager = packageManager;
mOperationStorage = operationStorage;
mTransportManager = transportManager;
mBaseStateDir = null;
mDataDir = null;
@@ -521,7 +523,6 @@ public class UserBackupManagerService {
mRunInitReceiver = null;
mRunInitIntent = null;
mAgentTimeoutParameters = null;
mTransportManager = null;
mActivityManagerInternal = null;
mAlarmManager = null;
mConstants = null;
@@ -3038,6 +3039,37 @@ public class UserBackupManagerService {
mBackupPreferences.addExcludedKeys(packageName, keys);
}
public void reportDelayedRestoreResult(String packageName,
List<BackupRestoreEventLogger.DataTypeResult> results) {
String transport = mTransportManager.getCurrentTransportName();
if (transport == null) {
Slog.w(TAG, "Failed to send delayed restore logs as no transport selected");
return;
}
TransportConnection transportConnection = null;
try {
PackageInfo packageInfo = getPackageManager().getPackageInfoAsUser(packageName,
PackageManager.PackageInfoFlags.of(/* value */ 0), getUserId());
transportConnection = mTransportManager.getTransportClientOrThrow(
transport, /* caller */"BMS.reportDelayedRestoreResult");
BackupTransportClient transportClient = transportConnection.connectOrThrow(
/* caller */ "BMS.reportDelayedRestoreResult");
IBackupManagerMonitor monitor = transportClient.getBackupManagerMonitor();
BackupManagerMonitorUtils.sendAgentLoggingResults(monitor, packageInfo, results);
} catch (NameNotFoundException | TransportNotAvailableException
| TransportNotRegisteredException | RemoteException e) {
Slog.w(TAG, "Failed to send delayed restore logs: " + e);
} finally {
if (transportConnection != null) {
mTransportManager.disposeOfTransportClient(transportConnection,
/* caller */"BMS.reportDelayedRestoreResult");
}
}
}
private boolean startConfirmationUi(int token, String action) {
try {
Intent confIntent = new Intent(action);

View File

@@ -24,10 +24,11 @@ import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.IBackupAgent;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.app.backup.IBackupManagerMonitor;
import android.content.pm.PackageInfo;
import android.os.Bundle;
@@ -119,19 +120,11 @@ public class BackupManagerMonitorUtils {
}
try {
AndroidFuture<List<BackupRestoreEventLogger.DataTypeResult>> resultsFuture =
AndroidFuture<List<DataTypeResult>> resultsFuture =
new AndroidFuture<>();
agent.getLoggerResults(resultsFuture);
Bundle loggerResultsBundle = new Bundle();
loggerResultsBundle.putParcelableList(
EXTRA_LOG_AGENT_LOGGING_RESULTS,
return sendAgentLoggingResults(monitor, pkg,
resultsFuture.get(AGENT_LOGGER_RESULTS_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
return BackupManagerMonitorUtils.monitorEvent(
monitor,
LOG_EVENT_ID_AGENT_LOGGING_RESULTS,
pkg,
LOG_EVENT_CATEGORY_AGENT,
loggerResultsBundle);
} catch (TimeoutException e) {
Slog.w(TAG, "Timeout while waiting to retrieve logging results from agent", e);
} catch (Exception e) {
@@ -140,6 +133,19 @@ public class BackupManagerMonitorUtils {
return monitor;
}
public static IBackupManagerMonitor sendAgentLoggingResults(
@NonNull IBackupManagerMonitor monitor, PackageInfo pkg, List<DataTypeResult> results) {
Bundle loggerResultsBundle = new Bundle();
loggerResultsBundle.putParcelableList(
EXTRA_LOG_AGENT_LOGGING_RESULTS, results);
return monitorEvent(
monitor,
LOG_EVENT_ID_AGENT_LOGGING_RESULTS,
pkg,
LOG_EVENT_CATEGORY_AGENT,
loggerResultsBundle);
}
/**
* Adds given key-value pair in the bundle and returns the bundle. If bundle was null it will
* be created.

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 The Android Open Source Project
* 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.
@@ -18,6 +18,9 @@ package com.android.server.backup;
import static com.google.common.truth.Truth.assertThat;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -25,11 +28,11 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.backup.BackupAgent;
import android.app.backup.BackupAnnotations.BackupDestination;
import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.content.Context;
@@ -37,6 +40,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.platform.test.annotations.Presubmit;
import android.util.FeatureFlagUtils;
import androidx.test.filters.FlakyTest;
import androidx.test.runner.AndroidJUnit4;
@@ -47,15 +51,21 @@ import com.android.server.backup.params.BackupParams;
import com.android.server.backup.transport.BackupTransportClient;
import com.android.server.backup.transport.TransportConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.BackupManagerMonitorUtils;
import com.google.common.collect.ImmutableSet;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import java.util.Arrays;
import java.util.List;
import java.util.function.IntConsumer;
@Presubmit
@@ -63,6 +73,7 @@ import java.util.function.IntConsumer;
public class UserBackupManagerServiceTest {
private static final String TEST_PACKAGE = "package1";
private static final String[] TEST_PACKAGES = new String[] { TEST_PACKAGE };
private static final String TEST_TRANSPORT = "transport";
private static final int WORKER_THREAD_TIMEOUT_MILLISECONDS = 1;
@Mock Context mContext;
@@ -70,21 +81,38 @@ public class UserBackupManagerServiceTest {
@Mock IBackupObserver mBackupObserver;
@Mock PackageManager mPackageManager;
@Mock TransportConnection mTransportConnection;
@Mock TransportManager mTransportManager;
@Mock BackupTransportClient mBackupTransport;
@Mock BackupEligibilityRules mBackupEligibilityRules;
@Mock LifecycleOperationStorage mOperationStorage;
private MockitoSession mSession;
private TestBackupService mService;
@Before
public void setUp() throws Exception {
mSession = mockitoSession()
.initMocks(this)
.mockStatic(BackupManagerMonitorUtils.class)
.mockStatic(FeatureFlagUtils.class)
// TODO(b/263239775): Remove unnecessary stubbing.
.strictness(Strictness.LENIENT)
.startMocking();
MockitoAnnotations.initMocks(this);
mService = new TestBackupService(mContext, mPackageManager, mOperationStorage);
mService = new TestBackupService(mContext, mPackageManager, mOperationStorage,
mTransportManager);
mService.setEnabled(true);
mService.setSetupComplete(true);
}
@After
public void tearDown() {
if (mSession != null) {
mSession.finishMocking();
}
}
@Test
public void initializeBackupEnableState_doesntWriteStateToDisk() {
mService.initializeBackupEnableState();
@@ -201,6 +229,26 @@ public class UserBackupManagerServiceTest {
.cancelOperation(anyInt(), anyBoolean(), any(IntConsumer.class));
}
@Test
public void testReportDelayedRestoreResult_sendsLogsToMonitor() throws Exception {
PackageInfo packageInfo = getPackageInfo(TEST_PACKAGE);
when(mPackageManager.getPackageInfoAsUser(anyString(),
any(PackageManager.PackageInfoFlags.class), anyInt())).thenReturn(packageInfo);
when(mTransportManager.getCurrentTransportName()).thenReturn(TEST_TRANSPORT);
when(mTransportManager.getTransportClientOrThrow(eq(TEST_TRANSPORT), anyString()))
.thenReturn(mTransportConnection);
when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransport);
when(mBackupTransport.getBackupManagerMonitor()).thenReturn(mBackupManagerMonitor);
List<DataTypeResult> results = Arrays.asList(new DataTypeResult(/* dataType */ "type_1"),
new DataTypeResult(/* dataType */ "type_2"));
mService.reportDelayedRestoreResult(TEST_PACKAGE, results);
verify(() -> BackupManagerMonitorUtils.sendAgentLoggingResults(
eq(mBackupManagerMonitor), eq(packageInfo), eq(results)));
}
private static PackageInfo getPackageInfo(String packageName) {
PackageInfo packageInfo = new PackageInfo();
packageInfo.applicationInfo = new ApplicationInfo();
@@ -215,8 +263,8 @@ public class UserBackupManagerServiceTest {
private volatile Thread mWorkerThread = null;
TestBackupService(Context context, PackageManager packageManager,
LifecycleOperationStorage operationStorage) {
super(context, packageManager, operationStorage);
LifecycleOperationStorage operationStorage, TransportManager transportManager) {
super(context, packageManager, operationStorage, transportManager);
}
@Override

View File

@@ -172,11 +172,30 @@ public class BackupManagerMonitorUtilsTest {
.when(agent)
.getLoggerResults(any());
IBackupManagerMonitor result =
IBackupManagerMonitor monitor =
BackupManagerMonitorUtils.monitorAgentLoggingResults(
mMonitorMock, packageInfo, agent);
assertThat(result).isEqualTo(mMonitorMock);
assertCorrectBundleSentToMonitor(monitor);
}
@Test
public void sendAgentLoggingResults_fillsBundleCorrectly() throws Exception {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = "test.package";
List<BackupRestoreEventLogger.DataTypeResult> loggingResults = new ArrayList<>();
loggingResults.add(new BackupRestoreEventLogger.DataTypeResult("testLoggingResult"));
IBackupManagerMonitor monitor = BackupManagerMonitorUtils.sendAgentLoggingResults(
mMonitorMock, packageInfo, loggingResults);
assertCorrectBundleSentToMonitor(monitor);
}
private void assertCorrectBundleSentToMonitor(IBackupManagerMonitor monitor) throws Exception {
assertThat(monitor).isEqualTo(mMonitorMock);
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(mMonitorMock).onEvent(bundleCaptor.capture());
Bundle eventBundle = bundleCaptor.getValue();