1 /*
2  * Copyright (C) 2020 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 android.app.backup;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.Mockito.when;
22 
23 import android.app.IBackupAgent;
24 import android.app.backup.BackupAgent.IncludeExcludeRules;
25 import android.app.backup.BackupAnnotations.BackupDestination;
26 import android.app.backup.BackupAnnotations.OperationType;
27 import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
28 import android.content.Context;
29 import android.os.ParcelFileDescriptor;
30 import android.os.UserHandle;
31 import android.platform.test.annotations.Presubmit;
32 import android.platform.test.flag.junit.SetFlagsRule;
33 import android.util.ArraySet;
34 
35 import androidx.test.runner.AndroidJUnit4;
36 
37 import com.android.server.backup.Flags;
38 
39 import org.junit.Before;
40 import org.junit.Rule;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 import org.mockito.Mock;
44 import org.mockito.MockitoAnnotations;
45 
46 import java.io.FileInputStream;
47 import java.io.FileOutputStream;
48 import java.io.IOException;
49 import java.nio.charset.StandardCharsets;
50 import java.util.Collections;
51 import java.util.Map;
52 import java.util.Set;
53 
54 @Presubmit
55 @RunWith(AndroidJUnit4.class)
56 public class BackupAgentTest {
57     // An arbitrary user.
58     private static final UserHandle USER_HANDLE = new UserHandle(15);
59     private static final String DATA_TYPE_BACKED_UP = "test data type";
60 
61     @Mock IBackupManager mIBackupManager;
62     @Mock FullBackup.BackupScheme mBackupScheme;
63     @Mock Context mContext;
64 
65     @Rule
66     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
67 
68     @Before
setUp()69     public void setUp() {
70         MockitoAnnotations.initMocks(this);
71     }
72 
73     @Test
testGetIncludeExcludeRules_isNotMigration_returnsRules()74     public void testGetIncludeExcludeRules_isNotMigration_returnsRules() throws Exception {
75         PathWithRequiredFlags path = new PathWithRequiredFlags("path", /* requiredFlags */ 0);
76         Map<String, Set<PathWithRequiredFlags>> includePaths = Collections.singletonMap("test",
77                 Collections.singleton(path));
78         ArraySet<PathWithRequiredFlags> excludePaths = new ArraySet<>();
79         excludePaths.add(path);
80         IncludeExcludeRules expectedRules = new IncludeExcludeRules(includePaths, excludePaths);
81 
82         BackupAgent backupAgent = getAgentForBackupDestination(BackupDestination.CLOUD);
83         when(mBackupScheme.maybeParseAndGetCanonicalExcludePaths()).thenReturn(excludePaths);
84         when(mBackupScheme.maybeParseAndGetCanonicalIncludePaths()).thenReturn(includePaths);
85 
86         IncludeExcludeRules rules = backupAgent.getIncludeExcludeRules(mBackupScheme);
87         assertThat(rules).isEqualTo(expectedRules);
88     }
89 
90     @Test
getBackupRestoreEventLogger_beforeOnCreate_isNull()91     public void getBackupRestoreEventLogger_beforeOnCreate_isNull() {
92         BackupAgent agent = new TestFullBackupAgent();
93 
94         assertThat(agent.getBackupRestoreEventLogger()).isNull();
95     }
96 
97     @Test
getBackupRestoreEventLogger_afterOnCreateForBackup_initializedForBackup()98     public void getBackupRestoreEventLogger_afterOnCreateForBackup_initializedForBackup() {
99         BackupAgent agent = new TestFullBackupAgent();
100         agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP);
101 
102         assertThat(agent.getBackupRestoreEventLogger().getOperationType()).isEqualTo(
103                 OperationType.BACKUP);
104     }
105 
106     @Test
getBackupRestoreEventLogger_afterOnCreateForRestore_initializedForRestore()107     public void getBackupRestoreEventLogger_afterOnCreateForRestore_initializedForRestore() {
108         BackupAgent agent = new TestFullBackupAgent();
109         agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.RESTORE);
110 
111         assertThat(agent.getBackupRestoreEventLogger().getOperationType()).isEqualTo(
112                 OperationType.RESTORE);
113     }
114 
115     @Test
getBackupRestoreEventLogger_afterBackup_containsLogsLoggedByAgent()116     public void getBackupRestoreEventLogger_afterBackup_containsLogsLoggedByAgent()
117             throws Exception {
118         BackupAgent agent = new TestFullBackupAgent();
119         agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP);
120 
121         // TestFullBackupAgent logs DATA_TYPE_BACKED_UP when onFullBackup is called.
122         agent.onFullBackup(new FullBackupDataOutput(/* quota = */ 0));
123 
124         assertThat(agent.getBackupRestoreEventLogger().getLoggingResults().get(0).getDataType())
125                 .isEqualTo(DATA_TYPE_BACKED_UP);
126     }
127 
128     @Test
testClearLogger_clearsPendingLogs()129     public void testClearLogger_clearsPendingLogs() throws Exception {
130         BackupAgent agent = new TestFullBackupAgent();
131         agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP);
132 
133         agent.onFullBackup(new FullBackupDataOutput(/* quota = */ 0));
134         agent.clearBackupRestoreEventLogger();
135 
136         assertThat(agent.getBackupRestoreEventLogger().getLoggingResults().size()).isEqualTo(0);
137     }
138 
139     @Test
testClearLoggerBetweenBackups_restartsSuccessCount()140     public void testClearLoggerBetweenBackups_restartsSuccessCount() throws Exception {
141         BackupAgent agent = new TestFullBackupAgent();
142         agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.BACKUP);
143 
144         agent.onFullBackup(new FullBackupDataOutput(/* quota = */ 0));
145         agent.clearBackupRestoreEventLogger();
146         agent.onFullBackup(new FullBackupDataOutput(/* quota = */ 0));
147 
148         assertThat(agent.getBackupRestoreEventLogger().getLoggingResults().get(
149                 0).getSuccessCount()).isEqualTo(1);
150     }
151 
152     @Test
doRestoreFile_agentOverrideIgnoresFile_consumesAllBytesInBuffer()153     public void doRestoreFile_agentOverrideIgnoresFile_consumesAllBytesInBuffer() throws Exception {
154         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_CLEAR_PIPE_AFTER_RESTORE_FILE);
155         BackupAgent agent = new TestRestoreIgnoringFullBackupAgent();
156         agent.attach(mContext);
157         agent.onCreate(USER_HANDLE, BackupDestination.CLOUD, OperationType.RESTORE);
158         IBackupAgent agentBinder = (IBackupAgent) agent.onBind();
159 
160         ParcelFileDescriptor[] pipes = ParcelFileDescriptor.createPipe();
161         FileOutputStream writeSide = new FileOutputStream(
162                 pipes[1].getFileDescriptor());
163         writeSide.write("Hello".getBytes(StandardCharsets.UTF_8));
164 
165         agentBinder.doRestoreFile(pipes[0], /* length= */ 5, BackupAgent.TYPE_FILE,
166                 FullBackup.FILES_TREE_TOKEN, /* path= */ "hello_file", /* mode= */
167                 0666, /* mtime= */ 12345, /* token= */ 6789, mIBackupManager);
168 
169         try (FileInputStream in = new FileInputStream(pipes[0].getFileDescriptor())) {
170             assertThat(in.available()).isEqualTo(0);
171         } finally {
172             pipes[0].close();
173             pipes[1].close();
174         }
175     }
176 
getAgentForBackupDestination(@ackupDestination int backupDestination)177     private BackupAgent getAgentForBackupDestination(@BackupDestination int backupDestination) {
178         BackupAgent agent = new TestFullBackupAgent();
179         agent.onCreate(USER_HANDLE, backupDestination);
180         return agent;
181     }
182 
183     private static class TestFullBackupAgent extends BackupAgent {
184         @Override
onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)185         public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
186                 ParcelFileDescriptor newState) throws IOException {
187             // Left empty as this is a full backup agent.
188         }
189 
190         @Override
onFullBackup(FullBackupDataOutput data)191         public void onFullBackup(FullBackupDataOutput data) {
192             getBackupRestoreEventLogger().logItemsBackedUp(DATA_TYPE_BACKED_UP, 1);
193         }
194 
195         @Override
onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)196         public void onRestore(BackupDataInput data, int appVersionCode,
197                 ParcelFileDescriptor newState) throws IOException {
198             // Left empty as this is a full backup agent.
199         }
200     }
201 
202     private static class TestRestoreIgnoringFullBackupAgent extends TestFullBackupAgent {
203 
204         @Override
onRestoreFile(ParcelFileDescriptor data, long size, int type, String domain, String path, long mode, long mtime)205         protected void onRestoreFile(ParcelFileDescriptor data, long size,
206                 int type, String domain, String path, long mode, long mtime)
207                 throws IOException {
208             // Ignore the file and don't consume any data.
209         }
210     }
211 }
212