1 /* 2 * Copyright (C) 2019 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.rollback; 18 19 import static org.junit.Assert.assertArrayEquals; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.mockito.ArgumentMatchers.eq; 25 import static org.mockito.Matchers.anyInt; 26 import static org.mockito.Matchers.anyString; 27 import static org.mockito.Mockito.doReturn; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.spy; 30 import static org.mockito.Mockito.when; 31 32 import android.content.pm.VersionedPackage; 33 import android.content.rollback.PackageRollbackInfo; 34 import android.content.rollback.PackageRollbackInfo.RestoreInfo; 35 import android.util.IntArray; 36 import android.util.SparseLongArray; 37 38 import com.android.server.pm.Installer; 39 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.junit.runners.JUnit4; 43 import org.mockito.InOrder; 44 import org.mockito.Mockito; 45 46 import java.io.File; 47 import java.util.ArrayList; 48 import java.util.Arrays; 49 import java.util.Set; 50 51 @RunWith(JUnit4.class) 52 public class AppDataRollbackHelperTest { 53 54 @Test testSnapshotAppData()55 public void testSnapshotAppData() throws Exception { 56 Installer installer = mock(Installer.class); 57 AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); 58 59 // All users are unlocked so we should snapshot data for them. 60 doReturn(true).when(helper).isUserCredentialLocked(eq(10)); 61 doReturn(true).when(helper).isUserCredentialLocked(eq(11)); 62 PackageRollbackInfo info = createPackageRollbackInfo("com.foo.bar"); 63 helper.snapshotAppData(5, info, new int[]{10, 11}); 64 65 assertEquals(2, info.getPendingBackups().size()); 66 assertEquals(10, info.getPendingBackups().get(0)); 67 assertEquals(11, info.getPendingBackups().get(1)); 68 69 assertEquals(0, info.getCeSnapshotInodes().size()); 70 71 InOrder inOrder = Mockito.inOrder(installer); 72 inOrder.verify(installer).snapshotAppData( 73 eq("com.foo.bar"), eq(10), eq(5), eq(Installer.FLAG_STORAGE_DE)); 74 inOrder.verify(installer).snapshotAppData( 75 eq("com.foo.bar"), eq(11), eq(5), eq(Installer.FLAG_STORAGE_DE)); 76 inOrder.verifyNoMoreInteractions(); 77 78 // One of the users is unlocked but the other isn't 79 doReturn(false).when(helper).isUserCredentialLocked(eq(10)); 80 doReturn(true).when(helper).isUserCredentialLocked(eq(11)); 81 when(installer.snapshotAppData(anyString(), anyInt(), anyInt(), anyInt())).thenReturn(239L); 82 83 PackageRollbackInfo info2 = createPackageRollbackInfo("com.foo.bar"); 84 helper.snapshotAppData(7, info2, new int[]{10, 11}); 85 assertEquals(1, info2.getPendingBackups().size()); 86 assertEquals(11, info2.getPendingBackups().get(0)); 87 88 assertEquals(1, info2.getCeSnapshotInodes().size()); 89 assertEquals(239L, info2.getCeSnapshotInodes().get(10)); 90 91 inOrder = Mockito.inOrder(installer); 92 inOrder.verify(installer).snapshotAppData( 93 eq("com.foo.bar"), eq(10), eq(7), 94 eq(Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE)); 95 inOrder.verify(installer).snapshotAppData( 96 eq("com.foo.bar"), eq(11), eq(7), eq(Installer.FLAG_STORAGE_DE)); 97 inOrder.verifyNoMoreInteractions(); 98 } 99 createPackageRollbackInfo(String packageName, final int[] installedUsers)100 private static PackageRollbackInfo createPackageRollbackInfo(String packageName, 101 final int[] installedUsers) { 102 return new PackageRollbackInfo( 103 new VersionedPackage(packageName, 2), new VersionedPackage(packageName, 1), 104 new IntArray(), new ArrayList<>(), false, IntArray.wrap(installedUsers), 105 new SparseLongArray()); 106 } 107 createPackageRollbackInfo(String packageName)108 private static PackageRollbackInfo createPackageRollbackInfo(String packageName) { 109 return createPackageRollbackInfo(packageName, new int[] {}); 110 } 111 112 @Test testRestoreAppDataSnapshot_pendingBackupForUser()113 public void testRestoreAppDataSnapshot_pendingBackupForUser() throws Exception { 114 Installer installer = mock(Installer.class); 115 AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); 116 117 PackageRollbackInfo info = createPackageRollbackInfo("com.foo"); 118 IntArray pendingBackups = info.getPendingBackups(); 119 pendingBackups.add(10); 120 pendingBackups.add(11); 121 122 assertTrue(helper.restoreAppData(13 /* rollbackId */, info, 10 /* userId */, 1 /* appId */, 123 "seinfo")); 124 125 // Should only require FLAG_STORAGE_DE here because we have a pending backup that we 126 // didn't manage to execute. 127 InOrder inOrder = Mockito.inOrder(installer); 128 inOrder.verify(installer).restoreAppDataSnapshot( 129 eq("com.foo"), eq(1) /* appId */, eq("seinfo"), eq(10) /* userId */, 130 eq(13) /* rollbackId */, eq(Installer.FLAG_STORAGE_DE)); 131 inOrder.verifyNoMoreInteractions(); 132 133 assertEquals(1, pendingBackups.size()); 134 assertEquals(11, pendingBackups.get(0)); 135 } 136 137 @Test testRestoreAppDataSnapshot_availableBackupForLockedUser()138 public void testRestoreAppDataSnapshot_availableBackupForLockedUser() throws Exception { 139 Installer installer = mock(Installer.class); 140 AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); 141 doReturn(true).when(helper).isUserCredentialLocked(eq(10)); 142 143 PackageRollbackInfo info = createPackageRollbackInfo("com.foo"); 144 145 assertTrue(helper.restoreAppData(73 /* rollbackId */, info, 10 /* userId */, 1 /* appId */, 146 "seinfo")); 147 148 InOrder inOrder = Mockito.inOrder(installer); 149 inOrder.verify(installer).restoreAppDataSnapshot( 150 eq("com.foo"), eq(1) /* appId */, eq("seinfo"), eq(10) /* userId */, 151 eq(73) /* rollbackId */, eq(Installer.FLAG_STORAGE_DE)); 152 inOrder.verifyNoMoreInteractions(); 153 154 ArrayList<RestoreInfo> pendingRestores = info.getPendingRestores(); 155 assertEquals(1, pendingRestores.size()); 156 assertEquals(10, pendingRestores.get(0).userId); 157 assertEquals(1, pendingRestores.get(0).appId); 158 assertEquals("seinfo", pendingRestores.get(0).seInfo); 159 } 160 161 @Test testRestoreAppDataSnapshot_availableBackupForUnlockedUser()162 public void testRestoreAppDataSnapshot_availableBackupForUnlockedUser() throws Exception { 163 Installer installer = mock(Installer.class); 164 AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); 165 doReturn(false).when(helper).isUserCredentialLocked(eq(10)); 166 167 PackageRollbackInfo info = createPackageRollbackInfo("com.foo"); 168 assertFalse(helper.restoreAppData(101 /* rollbackId */, info, 10 /* userId */, 169 1 /* appId */, "seinfo")); 170 171 InOrder inOrder = Mockito.inOrder(installer); 172 inOrder.verify(installer).restoreAppDataSnapshot( 173 eq("com.foo"), eq(1) /* appId */, eq("seinfo"), eq(10) /* userId */, 174 eq(101) /* rollbackId */, 175 eq(Installer.FLAG_STORAGE_DE | Installer.FLAG_STORAGE_CE)); 176 inOrder.verifyNoMoreInteractions(); 177 178 ArrayList<RestoreInfo> pendingRestores = info.getPendingRestores(); 179 assertEquals(0, pendingRestores.size()); 180 } 181 182 @Test destroyAppData()183 public void destroyAppData() throws Exception { 184 Installer installer = mock(Installer.class); 185 AppDataRollbackHelper helper = new AppDataRollbackHelper(installer); 186 187 PackageRollbackInfo info = createPackageRollbackInfo("com.foo.bar"); 188 info.putCeSnapshotInode(11, 239L); 189 helper.destroyAppDataSnapshot(5 /* rollbackId */, info, 10 /* userId */); 190 helper.destroyAppDataSnapshot(5 /* rollbackId */, info, 11 /* userId */); 191 192 InOrder inOrder = Mockito.inOrder(installer); 193 inOrder.verify(installer).destroyAppDataSnapshot( 194 eq("com.foo.bar"), eq(10) /* userId */, eq(0L) /* ceSnapshotInode */, 195 eq(5) /* rollbackId */, eq(Installer.FLAG_STORAGE_DE)); 196 inOrder.verify(installer).destroyAppDataSnapshot( 197 eq("com.foo.bar"), eq(11) /* userId */, eq(239L) /* ceSnapshotInode */, 198 eq(5) /* rollbackId */, eq(Installer.FLAG_STORAGE_DE | Installer.FLAG_STORAGE_CE)); 199 inOrder.verifyNoMoreInteractions(); 200 201 assertEquals(0, info.getCeSnapshotInodes().size()); 202 } 203 204 @Test commitPendingBackupAndRestoreForUser()205 public void commitPendingBackupAndRestoreForUser() throws Exception { 206 Installer installer = mock(Installer.class); 207 AppDataRollbackHelper helper = new AppDataRollbackHelper(installer); 208 209 when(installer.snapshotAppData(anyString(), anyInt(), anyInt(), anyInt())).thenReturn(53L); 210 211 // This one should be backed up. 212 PackageRollbackInfo pendingBackup = createPackageRollbackInfo("com.foo", new int[]{37, 73}); 213 pendingBackup.addPendingBackup(37); 214 215 // Nothing should be done for this one. 216 PackageRollbackInfo wasRecentlyRestored = createPackageRollbackInfo("com.bar", 217 new int[]{37, 73}); 218 wasRecentlyRestored.addPendingBackup(37); 219 wasRecentlyRestored.getPendingRestores().add( 220 new RestoreInfo(37 /* userId */, 239 /* appId*/, "seInfo")); 221 222 // This one should be restored 223 PackageRollbackInfo pendingRestore = createPackageRollbackInfo("com.abc", 224 new int[]{37, 73}); 225 pendingRestore.putCeSnapshotInode(37, 1543L); 226 pendingRestore.getPendingRestores().add( 227 new RestoreInfo(37 /* userId */, 57 /* appId*/, "seInfo")); 228 229 // This one shouldn't be processed, because it hasn't pending backups/restores for userId 230 // 37. 231 PackageRollbackInfo ignoredInfo = createPackageRollbackInfo("com.bar", 232 new int[]{3, 73}); 233 wasRecentlyRestored.addPendingBackup(3); 234 wasRecentlyRestored.addPendingBackup(73); 235 wasRecentlyRestored.getPendingRestores().add( 236 new RestoreInfo(73 /* userId */, 239 /* appId*/, "seInfo")); 237 238 Rollback dataWithPendingBackup = new Rollback(101, new File("/does/not/exist"), -1); 239 dataWithPendingBackup.info.getPackages().add(pendingBackup); 240 241 Rollback dataWithRecentRestore = new Rollback(17239, new File("/does/not/exist"), 242 -1); 243 dataWithRecentRestore.info.getPackages().add(wasRecentlyRestored); 244 245 Rollback dataForDifferentUser = new Rollback(17239, new File("/does/not/exist"), 246 -1); 247 dataForDifferentUser.info.getPackages().add(ignoredInfo); 248 249 Rollback dataForRestore = new Rollback(17239, new File("/does/not/exist"), -1); 250 dataForRestore.info.getPackages().add(pendingRestore); 251 dataForRestore.info.getPackages().add(wasRecentlyRestored); 252 253 Set<Rollback> changed = helper.commitPendingBackupAndRestoreForUser(37, 254 Arrays.asList(dataWithPendingBackup, dataWithRecentRestore, dataForDifferentUser, 255 dataForRestore)); 256 InOrder inOrder = Mockito.inOrder(installer); 257 258 // Check that pending backup and restore for the same package mutually destroyed each other. 259 assertEquals(-1, wasRecentlyRestored.getPendingBackups().indexOf(37)); 260 assertNull(wasRecentlyRestored.getRestoreInfo(37)); 261 262 // Check that backup was performed. 263 inOrder.verify(installer).snapshotAppData(eq("com.foo"), eq(37), eq(101), 264 eq(Installer.FLAG_STORAGE_CE)); 265 assertEquals(-1, pendingBackup.getPendingBackups().indexOf(37)); 266 assertEquals(53, pendingBackup.getCeSnapshotInodes().get(37)); 267 268 // Check that changed returns correct Rollback. 269 assertEquals(3, changed.size()); 270 assertTrue(changed.contains(dataWithPendingBackup)); 271 assertTrue(changed.contains(dataWithRecentRestore)); 272 assertTrue(changed.contains(dataForRestore)); 273 274 // Check that restore was performed. 275 inOrder.verify(installer).restoreAppDataSnapshot( 276 eq("com.abc"), eq(57) /* appId */, eq("seInfo"), eq(37) /* userId */, 277 eq(17239) /* rollbackId */, eq(Installer.FLAG_STORAGE_CE)); 278 assertNull(pendingRestore.getRestoreInfo(37)); 279 280 inOrder.verifyNoMoreInteractions(); 281 } 282 283 @Test snapshotAddDataSavesSnapshottedUsersToInfo()284 public void snapshotAddDataSavesSnapshottedUsersToInfo() { 285 Installer installer = mock(Installer.class); 286 AppDataRollbackHelper helper = new AppDataRollbackHelper(installer); 287 288 PackageRollbackInfo info = createPackageRollbackInfo("com.foo.bar"); 289 helper.snapshotAppData(5, info, new int[]{10, 11}); 290 291 assertArrayEquals(info.getSnapshottedUsers().toArray(), new int[]{10, 11}); 292 } 293 } 294