1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.backup; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 23 24 import static org.junit.Assert.fail; 25 import static org.mockito.ArgumentMatchers.any; 26 import static org.mockito.ArgumentMatchers.anyBoolean; 27 import static org.mockito.ArgumentMatchers.anyInt; 28 import static org.mockito.ArgumentMatchers.anyString; 29 import static org.mockito.ArgumentMatchers.eq; 30 import static org.mockito.Mockito.never; 31 import static org.mockito.Mockito.when; 32 33 import android.app.backup.BackupAgent; 34 import android.app.backup.BackupAnnotations; 35 import android.app.backup.BackupAnnotations.BackupDestination; 36 import android.app.backup.BackupRestoreEventLogger.DataTypeResult; 37 import android.app.backup.IBackupManagerMonitor; 38 import android.app.backup.IBackupObserver; 39 import android.content.Context; 40 import android.content.pm.ApplicationInfo; 41 import android.content.pm.PackageInfo; 42 import android.content.pm.PackageManager; 43 import android.platform.test.annotations.Presubmit; 44 import android.util.FeatureFlagUtils; 45 46 import androidx.test.filters.FlakyTest; 47 import androidx.test.runner.AndroidJUnit4; 48 49 import com.android.server.backup.internal.LifecycleOperationStorage; 50 import com.android.server.backup.internal.OnTaskFinishedListener; 51 import com.android.server.backup.params.BackupParams; 52 import com.android.server.backup.transport.BackupTransportClient; 53 import com.android.server.backup.transport.TransportConnection; 54 import com.android.server.backup.utils.BackupEligibilityRules; 55 import com.android.server.backup.utils.BackupManagerMonitorEventSender; 56 57 import com.google.common.collect.ImmutableSet; 58 59 import org.junit.After; 60 import org.junit.Before; 61 import org.junit.Test; 62 import org.junit.runner.RunWith; 63 import org.mockito.Mock; 64 import org.mockito.MockitoAnnotations; 65 import org.mockito.MockitoSession; 66 import org.mockito.quality.Strictness; 67 68 import java.util.Arrays; 69 import java.util.List; 70 import java.util.function.IntConsumer; 71 72 @Presubmit 73 @RunWith(AndroidJUnit4.class) 74 public class UserBackupManagerServiceTest { 75 private static final String TEST_PACKAGE = "package1"; 76 private static final String[] TEST_PACKAGES = new String[] { TEST_PACKAGE }; 77 private static final String TEST_TRANSPORT = "transport"; 78 private static final int WORKER_THREAD_TIMEOUT_MILLISECONDS = 1; 79 80 @Mock Context mContext; 81 @Mock IBackupManagerMonitor mBackupManagerMonitor; 82 @Mock IBackupObserver mBackupObserver; 83 @Mock PackageManager mPackageManager; 84 @Mock TransportConnection mTransportConnection; 85 @Mock TransportManager mTransportManager; 86 @Mock BackupTransportClient mBackupTransport; 87 @Mock BackupEligibilityRules mBackupEligibilityRules; 88 @Mock LifecycleOperationStorage mOperationStorage; 89 @Mock BackupManagerMonitorEventSender mBackupManagerMonitorEventSender; 90 91 private MockitoSession mSession; 92 private TestBackupService mService; 93 94 @Before setUp()95 public void setUp() throws Exception { 96 mSession = mockitoSession() 97 .initMocks(this) 98 .mockStatic(BackupManagerMonitorEventSender.class) 99 .mockStatic(FeatureFlagUtils.class) 100 // TODO(b/263239775): Remove unnecessary stubbing. 101 .strictness(Strictness.LENIENT) 102 .startMocking(); 103 MockitoAnnotations.initMocks(this); 104 105 mService = new TestBackupService(mContext, mPackageManager, mOperationStorage, 106 mTransportManager); 107 mService.setEnabled(true); 108 mService.setSetupComplete(true); 109 } 110 111 @After tearDown()112 public void tearDown() { 113 if (mSession != null) { 114 mSession.finishMocking(); 115 } 116 } 117 118 @Test initializeBackupEnableState_doesntWriteStateToDisk()119 public void initializeBackupEnableState_doesntWriteStateToDisk() { 120 mService.initializeBackupEnableState(); 121 122 assertThat(mService.isEnabledStatePersisted).isFalse(); 123 } 124 125 @Test updateBackupEnableState_writesStateToDisk()126 public void updateBackupEnableState_writesStateToDisk() { 127 mService.setBackupEnabled(true); 128 129 assertThat(mService.isEnabledStatePersisted).isTrue(); 130 } 131 132 @Test getRequestBackupParams_appIsEligibleForFullBackup()133 public void getRequestBackupParams_appIsEligibleForFullBackup() throws Exception { 134 when(mPackageManager.getPackageInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn( 135 getPackageInfo(TEST_PACKAGE)); 136 when(mBackupEligibilityRules.appIsEligibleForBackup(any())).thenReturn(true); 137 when(mBackupEligibilityRules.appGetsFullBackup(any())).thenReturn(true); 138 139 BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver, 140 mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules, 141 mTransportConnection, /* transportDirName */ "", OnTaskFinishedListener.NOP); 142 143 assertThat(params.kvPackages).isEmpty(); 144 assertThat(params.fullPackages).contains(TEST_PACKAGE); 145 assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules); 146 } 147 148 @Test getRequestBackupParams_appIsEligibleForKeyValueBackup()149 public void getRequestBackupParams_appIsEligibleForKeyValueBackup() throws Exception { 150 when(mPackageManager.getPackageInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn( 151 getPackageInfo(TEST_PACKAGE)); 152 when(mBackupEligibilityRules.appIsEligibleForBackup(any())).thenReturn(true); 153 when(mBackupEligibilityRules.appGetsFullBackup(any())).thenReturn(false); 154 155 BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver, 156 mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules, 157 mTransportConnection, /* transportDirName */ "", OnTaskFinishedListener.NOP); 158 159 assertThat(params.kvPackages).contains(TEST_PACKAGE); 160 assertThat(params.fullPackages).isEmpty(); 161 assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules); 162 } 163 164 @Test getRequestBackupParams_appIsNotEligibleForBackup()165 public void getRequestBackupParams_appIsNotEligibleForBackup() throws Exception { 166 when(mPackageManager.getPackageInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn( 167 getPackageInfo(TEST_PACKAGE)); 168 when(mBackupEligibilityRules.appIsEligibleForBackup(any())).thenReturn(false); 169 when(mBackupEligibilityRules.appGetsFullBackup(any())).thenReturn(false); 170 171 BackupParams params = mService.getRequestBackupParams(TEST_PACKAGES, mBackupObserver, 172 mBackupManagerMonitor, /* flags */ 0, mBackupEligibilityRules, 173 mTransportConnection, /* transportDirName */ "", OnTaskFinishedListener.NOP); 174 175 assertThat(params.kvPackages).isEmpty(); 176 assertThat(params.fullPackages).isEmpty(); 177 assertThat(params.mBackupEligibilityRules).isEqualTo(mBackupEligibilityRules); 178 } 179 180 @Test testGetBackupDestinationFromTransport_returnsCloudByDefault()181 public void testGetBackupDestinationFromTransport_returnsCloudByDefault() 182 throws Exception { 183 when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransport); 184 when(mBackupTransport.getTransportFlags()).thenReturn(0); 185 186 int backupDestination = mService.getBackupDestinationFromTransport(mTransportConnection); 187 188 assertThat(backupDestination).isEqualTo(BackupDestination.CLOUD); 189 } 190 191 @Test testGetBackupDestinationFromTransport_returnsDeviceTransferForD2dTransport()192 public void testGetBackupDestinationFromTransport_returnsDeviceTransferForD2dTransport() 193 throws Exception { 194 // This is a temporary flag to control the new behaviour until it's ready to be fully 195 // rolled out. 196 mService.shouldUseNewBackupEligibilityRules = true; 197 198 when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransport); 199 when(mBackupTransport.getTransportFlags()).thenReturn( 200 BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER); 201 202 int backupDestination = mService.getBackupDestinationFromTransport(mTransportConnection); 203 204 assertThat(backupDestination).isEqualTo(BackupDestination.DEVICE_TRANSFER); 205 } 206 207 @Test 208 @FlakyTest testAgentDisconnected_cancelsCurrentOperations()209 public void testAgentDisconnected_cancelsCurrentOperations() throws Exception { 210 when(mOperationStorage.operationTokensForPackage(eq("com.android.foo"))).thenReturn( 211 ImmutableSet.of(123, 456, 789) 212 ); 213 214 mService.agentDisconnected("com.android.foo"); 215 216 mService.waitForAsyncOperation(); 217 verify(mOperationStorage).cancelOperation(eq(123), eq(true), any(IntConsumer.class)); 218 verify(mOperationStorage).cancelOperation(eq(456), eq(true), any()); 219 verify(mOperationStorage).cancelOperation(eq(789), eq(true), any()); 220 } 221 222 @Test testAgentDisconnected_unknownPackageName_cancelsNothing()223 public void testAgentDisconnected_unknownPackageName_cancelsNothing() throws Exception { 224 when(mOperationStorage.operationTokensForPackage(eq("com.android.foo"))).thenReturn( 225 ImmutableSet.of() 226 ); 227 228 mService.agentDisconnected("com.android.foo"); 229 230 verify(mOperationStorage, never()) 231 .cancelOperation(anyInt(), anyBoolean(), any(IntConsumer.class)); 232 } 233 234 @Test testReportDelayedRestoreResult_sendsLogsToMonitor()235 public void testReportDelayedRestoreResult_sendsLogsToMonitor() throws Exception { 236 PackageInfo packageInfo = getPackageInfo(TEST_PACKAGE); 237 when(mPackageManager.getPackageInfoAsUser(anyString(), 238 any(PackageManager.PackageInfoFlags.class), anyInt())).thenReturn(packageInfo); 239 when(mTransportManager.getCurrentTransportName()).thenReturn(TEST_TRANSPORT); 240 when(mTransportManager.getTransportClientOrThrow(eq(TEST_TRANSPORT), anyString())) 241 .thenReturn(mTransportConnection); 242 when(mTransportConnection.connectOrThrow(any())).thenReturn(mBackupTransport); 243 when(mBackupTransport.getBackupManagerMonitor()).thenReturn(mBackupManagerMonitor); 244 245 246 List<DataTypeResult> results = Arrays.asList(new DataTypeResult(/* dataType */ "type_1"), 247 new DataTypeResult(/* dataType */ "type_2")); 248 mService.reportDelayedRestoreResult(TEST_PACKAGE, results); 249 250 251 verify(mBackupManagerMonitorEventSender).sendAgentLoggingResults( 252 eq(packageInfo), eq(results), eq(BackupAnnotations.OperationType.RESTORE)); 253 } 254 getPackageInfo(String packageName)255 private static PackageInfo getPackageInfo(String packageName) { 256 PackageInfo packageInfo = new PackageInfo(); 257 packageInfo.applicationInfo = new ApplicationInfo(); 258 packageInfo.packageName = packageName; 259 return packageInfo; 260 } 261 262 private class TestBackupService extends UserBackupManagerService { 263 boolean isEnabledStatePersisted = false; 264 boolean shouldUseNewBackupEligibilityRules = false; 265 266 private volatile Thread mWorkerThread = null; 267 TestBackupService(Context context, PackageManager packageManager, LifecycleOperationStorage operationStorage, TransportManager transportManager)268 TestBackupService(Context context, PackageManager packageManager, 269 LifecycleOperationStorage operationStorage, TransportManager transportManager) { 270 super(context, packageManager, operationStorage, transportManager); 271 } 272 273 @Override writeEnabledState(boolean enable)274 void writeEnabledState(boolean enable) { 275 isEnabledStatePersisted = true; 276 } 277 278 @Override readEnabledState()279 boolean readEnabledState() { 280 return false; 281 } 282 283 @Override updateStateOnBackupEnabled(boolean wasEnabled, boolean enable)284 void updateStateOnBackupEnabled(boolean wasEnabled, boolean enable) {} 285 286 @Override shouldUseNewBackupEligibilityRules()287 boolean shouldUseNewBackupEligibilityRules() { 288 return shouldUseNewBackupEligibilityRules; 289 } 290 291 @Override getThreadForAsyncOperation(String operationName, Runnable operation)292 Thread getThreadForAsyncOperation(String operationName, Runnable operation) { 293 mWorkerThread = super.getThreadForAsyncOperation(operationName, operation); 294 return mWorkerThread; 295 } 296 297 @Override getBMMEventSender(IBackupManagerMonitor monitor)298 BackupManagerMonitorEventSender getBMMEventSender(IBackupManagerMonitor monitor) { 299 return mBackupManagerMonitorEventSender; 300 } 301 waitForAsyncOperation()302 private void waitForAsyncOperation() { 303 if (mWorkerThread == null) { 304 return; 305 } 306 307 try { 308 mWorkerThread.join(/* millis */ WORKER_THREAD_TIMEOUT_MILLISECONDS); 309 } catch (InterruptedException e) { 310 fail("Failed waiting for worker thread to complete: " + e.getMessage()); 311 } 312 } 313 } 314 } 315