1/* 2 * Copyright (C) 2011 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 17package android.content.pm; 18 19import static android.os.storage.VolumeInfo.STATE_MOUNTED; 20 21import android.content.Context; 22import android.os.storage.StorageManager; 23import android.os.storage.VolumeInfo; 24import android.test.AndroidTestCase; 25import android.util.Log; 26 27import com.android.internal.content.PackageHelper; 28 29import org.mockito.Mockito; 30 31import java.io.File; 32import java.io.IOException; 33import java.util.ArrayList; 34import java.util.List; 35import java.util.UUID; 36 37public class PackageHelperTests extends AndroidTestCase { 38 private static final boolean localLOGV = true; 39 public static final String TAG = "PackageHelperTests"; 40 protected final String PREFIX = "android.content.pm"; 41 42 private static final String sInternalVolPath = "/data"; 43 private static final String sAdoptedVolPath = "/mnt/expand/123"; 44 private static final String sPublicVolPath = "/emulated"; 45 46 private static final String sInternalVolUuid = StorageManager.UUID_PRIVATE_INTERNAL; 47 private static final String sAdoptedVolUuid = "adopted"; 48 private static final String sPublicVolUuid = "emulated"; 49 50 private static final long sInternalSize = 20000; 51 private static final long sAdoptedSize = 10000; 52 private static final long sPublicSize = 1000000; 53 54 private static StorageManager sStorageManager; 55 56 private static StorageManager createStorageManagerMock() throws Exception { 57 VolumeInfo internalVol = new VolumeInfo("private", 58 VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/); 59 internalVol.path = sInternalVolPath; 60 internalVol.state = STATE_MOUNTED; 61 internalVol.fsUuid = sInternalVolUuid; 62 63 VolumeInfo adoptedVol = new VolumeInfo("adopted", 64 VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/); 65 adoptedVol.path = sAdoptedVolPath; 66 adoptedVol.state = STATE_MOUNTED; 67 adoptedVol.fsUuid = sAdoptedVolUuid; 68 69 VolumeInfo publicVol = new VolumeInfo("public", 70 VolumeInfo.TYPE_PUBLIC, null /*DiskInfo*/, null /*partGuid*/); 71 publicVol.state = STATE_MOUNTED; 72 publicVol.path = sPublicVolPath; 73 publicVol.fsUuid = sPublicVolUuid; 74 75 List<VolumeInfo> volumes = new ArrayList<>(); 76 volumes.add(internalVol); 77 volumes.add(adoptedVol); 78 volumes.add(publicVol); 79 80 StorageManager storageManager = Mockito.mock(StorageManager.class); 81 Mockito.when(storageManager.getVolumes()).thenReturn(volumes); 82 83 File internalFile = new File(sInternalVolPath); 84 File adoptedFile = new File(sAdoptedVolPath); 85 File publicFile = new File(sPublicVolPath); 86 UUID internalUuid = UUID.randomUUID(); 87 UUID adoptedUuid = UUID.randomUUID(); 88 UUID publicUuid = UUID.randomUUID(); 89 Mockito.when(storageManager.getStorageBytesUntilLow(internalFile)).thenReturn(sInternalSize); 90 Mockito.when(storageManager.getStorageBytesUntilLow(adoptedFile)).thenReturn(sAdoptedSize); 91 Mockito.when(storageManager.getStorageBytesUntilLow(publicFile)).thenReturn(sPublicSize); 92 Mockito.when(storageManager.getUuidForPath(Mockito.eq(internalFile))).thenReturn(internalUuid); 93 Mockito.when(storageManager.getUuidForPath(Mockito.eq(adoptedFile))).thenReturn(adoptedUuid); 94 Mockito.when(storageManager.getUuidForPath(Mockito.eq(publicFile))).thenReturn(publicUuid); 95 Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalUuid), Mockito.anyInt())) 96 .thenReturn(sInternalSize); 97 Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(adoptedUuid), Mockito.anyInt())) 98 .thenReturn(sAdoptedSize); 99 Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(publicUuid), Mockito.anyInt())) 100 .thenReturn(sPublicSize); 101 return storageManager; 102 } 103 104 private static final class MockedInterface extends PackageHelper.TestableInterface { 105 private boolean mForceAllowOnExternal = false; 106 private boolean mAllow3rdPartyOnInternal = true; 107 private ApplicationInfo mApplicationInfo = null; 108 109 public void setMockValues(ApplicationInfo applicationInfo, 110 boolean forceAllowOnExternal, boolean allow3rdPartyOnInternal) { 111 mForceAllowOnExternal = forceAllowOnExternal; 112 mAllow3rdPartyOnInternal = allow3rdPartyOnInternal; 113 mApplicationInfo = applicationInfo; 114 } 115 116 @Override 117 public StorageManager getStorageManager(Context context) { 118 return sStorageManager; 119 } 120 121 @Override 122 public boolean getForceAllowOnExternalSetting(Context context) { 123 return mForceAllowOnExternal; 124 } 125 126 @Override 127 public boolean getAllow3rdPartyOnInternalConfig(Context context) { 128 return mAllow3rdPartyOnInternal; 129 } 130 131 @Override 132 public ApplicationInfo getExistingAppInfo(Context context, String packagename) { 133 return mApplicationInfo; 134 } 135 136 @Override 137 public File getDataDirectory() { 138 return new File(sInternalVolPath); 139 } 140 } 141 142 @Override 143 protected void setUp() throws Exception { 144 super.setUp(); 145 sStorageManager = createStorageManagerMock(); 146 if (localLOGV) Log.i(TAG, "Cleaning out old test containers"); 147 } 148 149 @Override 150 protected void tearDown() throws Exception { 151 super.tearDown(); 152 sStorageManager = null; 153 if (localLOGV) Log.i(TAG, "Cleaning out old test containers"); 154 } 155 156 public void testResolveInstallVolumeInternal_SystemApp() throws IOException { 157 ApplicationInfo systemAppInfo = new ApplicationInfo(); 158 systemAppInfo.flags = ApplicationInfo.FLAG_SYSTEM; 159 160 // All test cases for when the system app fits on internal. 161 MockedInterface mockedInterface = new MockedInterface(); 162 mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/, 163 true /*allow 3rd party on internal*/); 164 String volume = null; 165 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 166 1 /*install location*/, 1000 /*size bytes*/, mockedInterface); 167 assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume); 168 169 mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/, 170 true /*allow 3rd party on internal*/); 171 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 172 1 /*install location*/, 1000 /*size bytes*/, mockedInterface); 173 assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume); 174 175 mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/, 176 false /*allow 3rd party on internal*/); 177 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 178 1 /*install location*/, 1000 /*size bytes*/, mockedInterface); 179 assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume); 180 181 mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/, 182 false /*allow 3rd party on internal*/); 183 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 184 1 /*install location*/, 1000 /*size bytes*/, mockedInterface); 185 assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume); 186 187 188 // All test cases for when the system app does not fit on internal. 189 // Exception should be thrown. 190 mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/, 191 true /*allow 3rd party on internal*/); 192 try { 193 PackageHelper.resolveInstallVolume(getContext(), "package.name", 194 1 /*install location*/, 1000000 /*size bytes*/, mockedInterface); 195 fail("Expected exception in resolveInstallVolume was not thrown"); 196 } catch(IOException e) { 197 // expected 198 } 199 200 mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/, 201 true /*allow 3rd party on internal*/); 202 try { 203 PackageHelper.resolveInstallVolume(getContext(), "package.name", 204 1 /*install location*/, 1000000 /*size bytes*/, mockedInterface); 205 fail("Expected exception in resolveInstallVolume was not thrown"); 206 } catch(IOException e) { 207 // expected 208 } 209 210 mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/, 211 false /*allow 3rd party on internal*/); 212 try { 213 PackageHelper.resolveInstallVolume(getContext(), "package.name", 214 1 /*install location*/, 1000000 /*size bytes*/, mockedInterface); 215 fail("Expected exception in resolveInstallVolume was not thrown"); 216 } catch(IOException e) { 217 // expected 218 } 219 220 mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/, 221 false /*allow 3rd party on internal*/); 222 try { 223 PackageHelper.resolveInstallVolume(getContext(), "package.name", 224 1 /*install location*/, 1000000 /*size bytes*/, mockedInterface); 225 fail("Expected exception in resolveInstallVolume was not thrown"); 226 } catch(IOException e) { 227 // expected 228 } 229 } 230 231 public void testResolveInstallVolumeInternal_3rdParty_existing_not_too_big() 232 throws IOException { 233 // Existing apps always stay on the same volume. 234 // Test cases for existing app on internal. 235 ApplicationInfo appInfo = new ApplicationInfo(); 236 appInfo.volumeUuid = sInternalVolUuid; 237 MockedInterface mockedInterface = new MockedInterface(); 238 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 239 true /*allow 3rd party on internal*/); 240 String volume = null; 241 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 242 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); 243 assertEquals(sInternalVolUuid, volume); 244 245 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 246 true /*allow 3rd party on internal*/); 247 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 248 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); 249 assertEquals(sInternalVolUuid, volume); 250 } 251 252 public void testResolveInstallVolumeInternal_3rdParty_existing_not_too_big_adopted() 253 throws IOException { 254 // Test cases for existing app on the adopted media. 255 ApplicationInfo appInfo = new ApplicationInfo(); 256 MockedInterface mockedInterface = new MockedInterface(); 257 String volume; 258 appInfo.volumeUuid = sAdoptedVolUuid; 259 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 260 true /*allow 3rd party on internal*/); 261 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 262 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); 263 assertEquals(sAdoptedVolUuid, volume); 264 265 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 266 true /*allow 3rd party on internal*/); 267 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 268 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); 269 assertEquals(sAdoptedVolUuid, volume); 270 271 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 272 false /*allow 3rd party on internal*/); 273 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 274 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); 275 assertEquals(sAdoptedVolUuid, volume); 276 277 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 278 false /*allow 3rd party on internal*/); 279 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 280 0 /*install location*/, 1000 /*size bytes*/, mockedInterface); 281 assertEquals(sAdoptedVolUuid, volume); 282 } 283 284 public void testResolveInstallVolumeAdopted_3rdParty_existing_too_big() { 285 // Test: update size too big, will throw exception. 286 ApplicationInfo appInfo = new ApplicationInfo(); 287 appInfo.volumeUuid = sAdoptedVolUuid; 288 289 MockedInterface mockedInterface = new MockedInterface(); 290 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 291 true /*allow 3rd party on internal*/); 292 try { 293 PackageHelper.resolveInstallVolume(getContext(), "package.name", 294 0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface); 295 fail("Expected exception was not thrown " + appInfo.volumeUuid); 296 } catch (IOException e) { 297 //expected 298 } 299 300 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 301 false /*allow 3rd party on internal*/); 302 try { 303 PackageHelper.resolveInstallVolume(getContext(), "package.name", 304 0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface); 305 fail("Expected exception was not thrown " + appInfo.volumeUuid); 306 } catch (IOException e) { 307 //expected 308 } 309 310 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 311 false /*allow 3rd party on internal*/); 312 try { 313 PackageHelper.resolveInstallVolume(getContext(), "package.name", 314 0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface); 315 fail("Expected exception was not thrown " + appInfo.volumeUuid); 316 } catch (IOException e) { 317 //expected 318 } 319 320 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 321 true /*allow 3rd party on internal*/); 322 try { 323 PackageHelper.resolveInstallVolume(getContext(), "package.name", 324 0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface); 325 fail("Expected exception was not thrown " + appInfo.volumeUuid); 326 } catch (IOException e) { 327 //expected 328 } 329 } 330 331 public void testResolveInstallVolumeInternal_3rdParty_auto() throws IOException { 332 ApplicationInfo appInfo = null; 333 MockedInterface mockedInterface = new MockedInterface(); 334 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 335 true /*allow 3rd party on internal*/); 336 String volume = null; 337 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 338 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); 339 // Should return the volume with bigger available space. 340 assertEquals(sInternalVolUuid, volume); 341 342 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 343 true /*allow 3rd party on internal*/); 344 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 345 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); 346 // Should return the volume with bigger available space. 347 assertEquals(sInternalVolUuid, volume); 348 349 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 350 false /*allow 3rd party on internal*/); 351 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 352 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); 353 // Should return the volume with bigger available space. 354 assertEquals(sAdoptedVolUuid, volume); 355 356 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 357 false /*allow 3rd party on internal*/); 358 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 359 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); 360 // Should return the volume with bigger available space. 361 assertEquals(sAdoptedVolUuid, volume); 362 363 364 } 365 366 public void testResolveInstallVolumeInternal_3rdParty_internal_only() throws IOException { 367 ApplicationInfo appInfo = null; 368 MockedInterface mockedInterface = new MockedInterface(); 369 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 370 true /*allow 3rd party on internal*/); 371 String volume = null; 372 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 373 1 /*install location internal ONLY*/, 1000 /*size bytes*/, mockedInterface); 374 assertEquals(sInternalVolUuid, volume); 375 376 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 377 true /*allow 3rd party on internal*/); 378 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 379 1 /*install location internal ONLY*/, 1000 /*size bytes*/, mockedInterface); 380 assertEquals(sInternalVolUuid, volume); 381 382 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 383 false /*allow 3rd party on internal*/); 384 try { 385 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 386 1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface); 387 fail("Expected exception in resolveInstallVolume was not thrown"); 388 } catch (IOException e) { 389 //expected 390 } 391 392 appInfo = null; 393 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 394 false /*allow 3rd party on internal*/); 395 volume = null; 396 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 397 1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface); 398 assertEquals(sAdoptedVolUuid, volume); 399 } 400 401 public void testResolveInstallVolumeInternal_3rdParty_not_allowed_on_internal() 402 throws IOException { 403 ApplicationInfo appInfo = null; 404 MockedInterface mockedInterface = new MockedInterface(); 405 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 406 false /*allow 3rd party on internal*/); 407 String volume = null; 408 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 409 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); 410 // Should return the non-internal volume. 411 assertEquals(sAdoptedVolUuid, volume); 412 413 appInfo = null; 414 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 415 false /*allow 3rd party on internal*/); 416 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 417 0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface); 418 // Should return the non-internal volume. 419 assertEquals(sAdoptedVolUuid, volume); 420 } 421 422 public void testResolveInstallVolumeInternal_3rdParty_internal_only_too_big() { 423 ApplicationInfo appInfo = null; 424 MockedInterface mockedInterface = new MockedInterface(); 425 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 426 true /*allow 3rd party on internal*/); 427 String volume = null; 428 try { 429 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 430 1 /*install location internal ONLY*/, 431 1000000 /*size too big*/, mockedInterface); 432 fail("Expected exception in resolveInstallVolume was not thrown"); 433 } catch (IOException e) { 434 //expected 435 } 436 } 437 438 public void testResolveInstallVolumeInternal_3rdParty_internal_only_not_allowed() 439 throws IOException { 440 ApplicationInfo appInfo = null; 441 MockedInterface mockedInterface = new MockedInterface(); 442 mockedInterface.setMockValues(appInfo, false /*force allow on external*/, 443 false /*allow 3rd party on internal*/); 444 String volume = null; 445 try { 446 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 447 1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface); 448 fail("Expected exception in resolveInstallVolume was not thrown"); 449 } catch (IOException e) { 450 //expected 451 } 452 453 appInfo = null; 454 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 455 false /*allow 3rd party on internal*/); 456 volume = null; 457 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 458 1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface); 459 assertEquals(sAdoptedVolUuid, volume); 460 461 } 462 463 public void testResolveInstallVolumeInternal_3rdParty_internal_only_forced_to_external() 464 throws IOException { 465 // New/existing installation: New 466 // app request location: Internal Only 467 // 3rd party allowed on internal: False 468 // Force allow external in setting: True 469 // Size fit? Yes 470 ApplicationInfo appInfo = null; 471 MockedInterface mockedInterface = new MockedInterface(); 472 mockedInterface.setMockValues(appInfo, true /*force allow on external*/, 473 false /*allow 3rd party on internal*/); 474 String volume = null; 475 volume = PackageHelper.resolveInstallVolume(getContext(), "package.name", 476 1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface); 477 assertEquals(sAdoptedVolUuid, volume); 478 } 479} 480