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:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user