1c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi/*
2c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi * Copyright (C) 2016 The Android Open Source Project
3c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi *
4c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi * Licensed under the Apache License, Version 2.0 (the "License");
5c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi * you may not use this file except in compliance with the License.
6c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi * You may obtain a copy of the License at
7c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi *
8c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi *      http://www.apache.org/licenses/LICENSE-2.0
9c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi *
10c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi * Unless required by applicable law or agreed to in writing, software
11c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi * distributed under the License is distributed on an "AS IS" BASIS,
12c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi * See the License for the specific language governing permissions and
14c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi * limitations under the License.
15c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi */
16c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
17c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishipackage com.android.server.storage;
18c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
19d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishiimport android.app.usage.StorageStats;
20d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishiimport android.app.usage.StorageStatsManager;
21c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport android.content.pm.UserInfo;
22c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport android.content.Context;
23c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport android.content.pm.ApplicationInfo;
24c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport android.content.pm.IPackageStatsObserver;
25c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport android.content.pm.PackageManager;
26c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport android.content.pm.PackageStats;
27d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishiimport android.os.UserHandle;
28c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport android.os.UserManager;
29c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport android.os.storage.VolumeInfo;
30c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport android.test.AndroidTestCase;
31579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishiimport android.util.ArrayMap;
32579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishiimport android.util.Log;
33579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi
34c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport org.junit.Before;
35c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport org.junit.Test;
36c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport org.junit.runner.RunWith;
37c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport org.junit.runners.JUnit4;
38c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport org.mockito.Mock;
39c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport org.mockito.Mockito;
40c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport org.mockito.MockitoAnnotations;
41c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport org.mockito.invocation.InvocationOnMock;
42c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport org.mockito.stubbing.Answer;
43c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
44c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport java.util.ArrayList;
45c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport java.util.List;
46579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishiimport java.util.Map;
47c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport java.util.concurrent.CountDownLatch;
48c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport java.util.concurrent.TimeUnit;
49c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
50c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport static com.google.common.truth.Truth.assertThat;
51c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport static org.mockito.Matchers.any;
52c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport static org.mockito.Matchers.anyInt;
53579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishiimport static org.mockito.Matchers.anyString;
54c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport static org.mockito.Matchers.eq;
55c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport static org.mockito.Mockito.doAnswer;
56c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishiimport static org.mockito.Mockito.when;
57c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
58c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi@RunWith(JUnit4.class)
59c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishipublic class AppCollectorTest extends AndroidTestCase {
60c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    private static final long TIMEOUT = TimeUnit.MINUTES.toMillis(1);
61c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    @Mock private Context mContext;
62c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    @Mock private PackageManager mPm;
63c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    @Mock private UserManager mUm;
64d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishi    @Mock private StorageStatsManager mSsm;
65c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    private List<UserInfo> mUsers;
66579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi    private Map<Integer, List<ApplicationInfo>> mUserApps;
67c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
68c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    @Before
69c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    public void setUp() throws Exception {
70c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        super.setUp();
71c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        MockitoAnnotations.initMocks(this);
72c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        when(mContext.getPackageManager()).thenReturn(mPm);
73c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUm);
74d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishi        when(mContext.getSystemService(Context.STORAGE_STATS_SERVICE)).thenReturn(mSsm);
75c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
76c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        // Set up the app list.
77579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        doAnswer((InvocationOnMock invocation) -> {
78579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi            Integer userId = (Integer) invocation.getArguments()[1];
79579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi            return mUserApps.get(userId);
80579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        }).when(mPm).getInstalledApplicationsAsUser(anyInt(), anyInt());
81c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
82c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        // Set up the user list with a single user (0).
83c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        mUsers = new ArrayList<>();
84c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        mUsers.add(new UserInfo(0, "", 0));
85579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi
86579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        mUserApps = new ArrayMap<>();
87579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        mUserApps.put(0, new ArrayList<>());
88c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        when(mUm.getUsers()).thenReturn(mUsers);
89c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    }
90c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
91c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    @Test
92c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    public void testNoApps() throws Exception {
93c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        VolumeInfo volume = new VolumeInfo("testuuid", 0, null, null);
94c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        volume.fsUuid = "testuuid";
95c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        AppCollector collector = new AppCollector(mContext, volume);
96c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
97c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        assertThat(collector.getPackageStats(TIMEOUT)).isEmpty();
98c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    }
99c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
100c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    @Test
101c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    public void testAppOnExternalVolume() throws Exception {
102579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        addApplication("com.test.app", "differentuuid", 0);
103c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        VolumeInfo volume = new VolumeInfo("testuuid", 0, null, null);
104c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        volume.fsUuid = "testuuid";
105c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        AppCollector collector = new AppCollector(mContext, volume);
106c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
107c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        assertThat(collector.getPackageStats(TIMEOUT)).isEmpty();
108c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    }
109c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
110c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    @Test
111c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    public void testOneValidApp() throws Exception {
112579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        addApplication("com.test.app", "testuuid", 0);
113c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        VolumeInfo volume = new VolumeInfo("testuuid", 0, null, null);
114c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        volume.fsUuid = "testuuid";
115c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        AppCollector collector = new AppCollector(mContext, volume);
116c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        PackageStats stats = new PackageStats("com.test.app");
117c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
118d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishi        when(mSsm.queryStatsForPackage(eq("testuuid"),
119d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishi                eq("com.test.app"), eq(UserHandle.of(0)))).thenReturn(new StorageStats());
120d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishi        assertThat(collector.getPackageStats(TIMEOUT)).containsExactly(stats);
121c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    }
122c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
123c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    @Test
124c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    public void testMultipleUsersOneApp() throws Exception {
125579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        addApplication("com.test.app", "testuuid", 0);
126579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        mUserApps.put(1, new ArrayList<>());
127579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        addApplication("com.test.app", "testuuid", 1);
128c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        mUsers.add(new UserInfo(1, "", 0));
129c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
130c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        VolumeInfo volume = new VolumeInfo("testuuid", 0, null, null);
131c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        volume.fsUuid = "testuuid";
132c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        AppCollector collector = new AppCollector(mContext, volume);
133c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        PackageStats stats = new PackageStats("com.test.app");
134c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        PackageStats otherStats = new PackageStats("com.test.app");
135c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        otherStats.userHandle = 1;
136c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
137d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishi        when(mSsm.queryStatsForPackage(eq("testuuid"),
138d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishi                eq("com.test.app"), eq(UserHandle.of(0)))).thenReturn(new StorageStats());
139d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishi        when(mSsm.queryStatsForPackage(eq("testuuid"),
140d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishi                eq("com.test.app"), eq(UserHandle.of(1)))).thenReturn(new StorageStats());
141d7b0329ab7c3355c84f8227939e88f305dd78b51Daniel Nishi        assertThat(collector.getPackageStats(TIMEOUT)).containsExactly(stats, otherStats);
142c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    }
143c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi
144d54f3a487bba1dc5008e2e1482ef451a59500752Daniel Nishi    @Test(expected=NullPointerException.class)
145d54f3a487bba1dc5008e2e1482ef451a59500752Daniel Nishi    public void testNullVolumeShouldCauseNPE() throws Exception {
146d54f3a487bba1dc5008e2e1482ef451a59500752Daniel Nishi        AppCollector collector = new AppCollector(mContext, null);
147d54f3a487bba1dc5008e2e1482ef451a59500752Daniel Nishi    }
148d54f3a487bba1dc5008e2e1482ef451a59500752Daniel Nishi
149579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi    @Test
150579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi    public void testAppNotFoundDoesntCauseCrash() throws Exception {
151579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        VolumeInfo volume = new VolumeInfo("testuuid", 0, null, null);
152579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        addApplication("com.test.app", "uuid", 0);
153579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        mUsers.add(new UserInfo(1, "", 0));
154579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        mUserApps.put(1, new ArrayList<>());
155579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        AppCollector collector = new AppCollector(mContext, volume);
156579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        when(mSsm.queryStatsForPackage(anyString(), anyString(), any(UserHandle.class))).thenThrow(
157579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi                new IllegalStateException());
158579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi
159579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        assertThat(collector.getPackageStats(TIMEOUT)).isEmpty();
160579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi    }
161579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi
162579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi    private void addApplication(String packageName, String uuid, int userId) {
163c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        ApplicationInfo info = new ApplicationInfo();
164c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        info.packageName = packageName;
165c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi        info.volumeUuid = uuid;
166579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        List<ApplicationInfo> userApps = mUserApps.get(userId);
167579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        if (userApps == null) {
168579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi            userApps = new ArrayList<>();
169579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi            mUserApps.put(userId, userApps);
170579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        }
171579e5581d5339dbdc79b9176fcc2fad660d155dbDaniel Nishi        userApps.add(info);
172c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi    }
173c7d9de59bfa083472a5c8fcfc02c2363b33e204fDaniel Nishi}
174