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