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