1/*
2 * Copyright (C) 2017 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.app;
18
19import android.content.Context;
20import android.content.pm.ApplicationInfo;
21import android.content.pm.IPackageManager;
22import android.content.pm.PackageInfo;
23import android.os.storage.StorageManager;
24import android.os.storage.VolumeInfo;
25
26import junit.framework.TestCase;
27
28import org.mockito.Mockito;
29
30import java.util.ArrayList;
31import java.util.Arrays;
32import java.util.List;
33
34import static android.os.storage.VolumeInfo.STATE_MOUNTED;
35import static android.os.storage.VolumeInfo.STATE_UNMOUNTED;
36
37public class ApplicationPackageManagerTest extends TestCase {
38    private static final String sInternalVolPath = "/data";
39    private static final String sAdoptedVolPath = "/mnt/expand/123";
40    private static final String sPublicVolPath = "/emulated";
41    private static final String sPrivateUnmountedVolPath = "/private";
42
43    private static final String sInternalVolUuid = null; //StorageManager.UUID_PRIVATE_INTERNAL
44    private static final String sAdoptedVolUuid = "adopted";
45    private static final String sPublicVolUuid = "emulated";
46    private static final String sPrivateUnmountedVolUuid = "private";
47
48    private static final VolumeInfo sInternalVol = new VolumeInfo("private",
49            VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/);
50
51    private static final VolumeInfo sAdoptedVol = new VolumeInfo("adopted",
52            VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/);
53
54    private static final VolumeInfo sPublicVol = new VolumeInfo("public",
55            VolumeInfo.TYPE_PUBLIC, null /*DiskInfo*/, null /*partGuid*/);
56
57    private static final VolumeInfo sPrivateUnmountedVol = new VolumeInfo("private2",
58            VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/);
59
60    private static final List<VolumeInfo> sVolumes = new ArrayList<>();
61
62    static {
63        sInternalVol.path = sInternalVolPath;
64        sInternalVol.state = STATE_MOUNTED;
65        sInternalVol.fsUuid = sInternalVolUuid;
66
67        sAdoptedVol.path = sAdoptedVolPath;
68        sAdoptedVol.state = STATE_MOUNTED;
69        sAdoptedVol.fsUuid = sAdoptedVolUuid;
70
71        sPublicVol.state = STATE_MOUNTED;
72        sPublicVol.path = sPublicVolPath;
73        sPublicVol.fsUuid = sPublicVolUuid;
74
75        sPrivateUnmountedVol.state = STATE_UNMOUNTED;
76        sPrivateUnmountedVol.path = sPrivateUnmountedVolPath;
77        sPrivateUnmountedVol.fsUuid = sPrivateUnmountedVolUuid;
78
79        sVolumes.add(sInternalVol);
80        sVolumes.add(sAdoptedVol);
81        sVolumes.add(sPublicVol);
82        sVolumes.add(sPrivateUnmountedVol);
83    }
84
85    private static final class MockedApplicationPackageManager extends ApplicationPackageManager {
86        private boolean mForceAllowOnExternal = false;
87        private boolean mAllow3rdPartyOnInternal = true;
88
89        public MockedApplicationPackageManager() {
90            super(null, null);
91        }
92
93        public void setForceAllowOnExternal(boolean forceAllowOnExternal) {
94            mForceAllowOnExternal = forceAllowOnExternal;
95        }
96
97        public void setAllow3rdPartyOnInternal(boolean allow3rdPartyOnInternal) {
98            mAllow3rdPartyOnInternal = allow3rdPartyOnInternal;
99        }
100
101        @Override
102        public boolean isForceAllowOnExternal(Context context) {
103            return mForceAllowOnExternal;
104        }
105
106        @Override
107        public boolean isAllow3rdPartyOnInternal(Context context) {
108            return mAllow3rdPartyOnInternal;
109        }
110    }
111
112    private StorageManager getMockedStorageManager() {
113        StorageManager storageManager = Mockito.mock(StorageManager.class);
114        Mockito.when(storageManager.getVolumes()).thenReturn(sVolumes);
115        Mockito.when(storageManager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL))
116                .thenReturn(sInternalVol);
117        Mockito.when(storageManager.findVolumeByUuid(sAdoptedVolUuid))
118                .thenReturn(sAdoptedVol);
119        Mockito.when(storageManager.findVolumeByUuid(sPublicVolUuid))
120                .thenReturn(sPublicVol);
121        Mockito.when(storageManager.findVolumeByUuid(sPrivateUnmountedVolUuid))
122                .thenReturn(sPrivateUnmountedVol);
123        return storageManager;
124    }
125
126    private void verifyReturnedVolumes(List<VolumeInfo> actualVols, VolumeInfo... exptectedVols) {
127        boolean failed = false;
128        if (actualVols.size() != exptectedVols.length) {
129            failed = true;
130        } else {
131            for (VolumeInfo vol : exptectedVols) {
132                if (!actualVols.contains(vol)) {
133                    failed = true;
134                    break;
135                }
136            }
137        }
138
139        if (failed) {
140            fail("Wrong volumes returned.\n Expected: " + Arrays.toString(exptectedVols)
141                    + "\n Actual: " + Arrays.toString(actualVols.toArray()));
142        }
143    }
144
145    public void testGetCandidateVolumes_systemApp() throws Exception {
146        ApplicationInfo sysAppInfo = new ApplicationInfo();
147        sysAppInfo.flags = ApplicationInfo.FLAG_SYSTEM;
148
149        StorageManager storageManager = getMockedStorageManager();
150        IPackageManager pm = Mockito.mock(IPackageManager.class);
151
152        MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
153
154        appPkgMgr.setAllow3rdPartyOnInternal(true);
155        appPkgMgr.setForceAllowOnExternal(true);
156        List<VolumeInfo> candidates =
157                appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm);
158        verifyReturnedVolumes(candidates, sInternalVol);
159
160        appPkgMgr.setAllow3rdPartyOnInternal(true);
161        appPkgMgr.setForceAllowOnExternal(false);
162        candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm);
163        verifyReturnedVolumes(candidates, sInternalVol);
164
165        appPkgMgr.setAllow3rdPartyOnInternal(false);
166        appPkgMgr.setForceAllowOnExternal(false);
167        candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm);
168        verifyReturnedVolumes(candidates, sInternalVol);
169
170        appPkgMgr.setAllow3rdPartyOnInternal(false);
171        appPkgMgr.setForceAllowOnExternal(true);
172        candidates = appPkgMgr.getPackageCandidateVolumes(sysAppInfo, storageManager, pm);
173        verifyReturnedVolumes(candidates, sInternalVol);
174    }
175
176    public void testGetCandidateVolumes_3rdParty_internalOnly() throws Exception {
177        ApplicationInfo appInfo = new ApplicationInfo();
178        StorageManager storageManager = getMockedStorageManager();
179
180        IPackageManager pm = Mockito.mock(IPackageManager.class);
181        Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false);
182
183        MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
184
185        // must allow 3rd party on internal, otherwise the app wouldn't have been installed before.
186        appPkgMgr.setAllow3rdPartyOnInternal(true);
187
188        // INSTALL_LOCATION_INTERNAL_ONLY AND INSTALL_LOCATION_UNSPECIFIED are treated the same.
189        int[] locations = {PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY,
190                PackageInfo.INSTALL_LOCATION_UNSPECIFIED};
191
192        for (int location : locations) {
193            appInfo.installLocation = location;
194            appPkgMgr.setForceAllowOnExternal(true);
195            List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes(
196                    appInfo, storageManager, pm);
197            verifyReturnedVolumes(candidates, sInternalVol, sAdoptedVol);
198
199            appPkgMgr.setForceAllowOnExternal(false);
200            candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm);
201            verifyReturnedVolumes(candidates, sInternalVol);
202        }
203    }
204
205    public void testGetCandidateVolumes_3rdParty_auto() throws Exception {
206        ApplicationInfo appInfo = new ApplicationInfo();
207        StorageManager storageManager = getMockedStorageManager();
208
209        IPackageManager pm = Mockito.mock(IPackageManager.class);
210
211        MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
212        appPkgMgr.setForceAllowOnExternal(true);
213
214        // INSTALL_LOCATION_AUTO AND INSTALL_LOCATION_PREFER_EXTERNAL are treated the same.
215        int[] locations = {PackageInfo.INSTALL_LOCATION_AUTO,
216                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL};
217
218        for (int location : locations) {
219            appInfo.installLocation = location;
220            appInfo.flags = 0;
221
222            appInfo.volumeUuid = sInternalVolUuid;
223            Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false);
224            appPkgMgr.setAllow3rdPartyOnInternal(true);
225            List<VolumeInfo> candidates = appPkgMgr.getPackageCandidateVolumes(
226                    appInfo, storageManager, pm);
227            verifyReturnedVolumes(candidates, sInternalVol, sAdoptedVol);
228
229            appInfo.volumeUuid = sInternalVolUuid;
230            appPkgMgr.setAllow3rdPartyOnInternal(true);
231            Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(true);
232            candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm);
233            verifyReturnedVolumes(candidates, sInternalVol);
234
235            appInfo.flags = ApplicationInfo.FLAG_EXTERNAL_STORAGE;
236            appInfo.volumeUuid = sAdoptedVolUuid;
237            appPkgMgr.setAllow3rdPartyOnInternal(false);
238            candidates = appPkgMgr.getPackageCandidateVolumes(appInfo, storageManager, pm);
239            verifyReturnedVolumes(candidates, sAdoptedVol);
240        }
241    }
242}
243