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