1/*
2 * Copyright (C) 2016 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 com.android.settingslib.users;
18
19import static org.mockito.Matchers.any;
20import static org.mockito.Matchers.anyInt;
21import static org.mockito.Matchers.argThat;
22import static org.mockito.Matchers.eq;
23import static org.mockito.Matchers.nullable;
24import static org.mockito.Mockito.mock;
25import static org.mockito.Mockito.times;
26import static org.mockito.Mockito.when;
27import static org.mockito.Mockito.verify;
28
29import android.appwidget.AppWidgetManager;
30import android.content.Context;
31import android.content.Intent;
32import android.content.pm.ActivityInfo;
33import android.content.pm.ApplicationInfo;
34import android.content.pm.IPackageDeleteObserver;
35import android.content.pm.IPackageManager;
36import android.content.pm.PackageInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.ResolveInfo;
39import android.content.pm.ServiceInfo;
40import android.content.pm.UserInfo;
41import android.os.RemoteException;
42import android.os.UserHandle;
43import android.os.UserManager;
44import android.test.suitebuilder.annotation.SmallTest;
45import android.view.inputmethod.InputMethodInfo;
46import com.android.settingslib.BaseTest;
47
48import org.mockito.ArgumentMatcher;
49import org.mockito.Mock;
50import org.mockito.MockitoAnnotations;
51
52import java.util.ArrayList;
53import java.util.List;
54
55@SmallTest
56public class AppRestrictionsHelperTest extends BaseTest {
57    private @Mock Context mContext;
58    private @Mock PackageManager mPm;
59    private @Mock IPackageManager mIpm;
60    private @Mock UserManager mUm;
61
62    private TestInjector mInjector;
63    private UserHandle mTestUser = UserHandle.of(1111);
64    private AppRestrictionsHelper mHelper;
65
66    private ArrayList<String> mInstalledApps;
67    private ArrayList<ApplicationInfo> mInstalledAppInfos;
68
69    @Override
70    protected void setUp() throws Exception {
71        super.setUp();
72        MockitoAnnotations.initMocks(this);
73
74        mInjector = new TestInjector();
75        final UserInfo user = new UserInfo(
76                mTestUser.getIdentifier(), "test_user", UserInfo.FLAG_RESTRICTED);
77        when(mUm.getUserInfo(mTestUser.getIdentifier())).thenReturn(user);
78        mHelper = new AppRestrictionsHelper(mInjector);
79        mInstalledApps = new ArrayList<>();
80        mInstalledAppInfos = new ArrayList<>();
81    }
82
83    public void testFetchAndMergeApps() throws Exception {
84        addSystemAppsWithRequiredAccounts("sys.app0");
85        addsystemImes(new String[] {"sys.app1", "sys.app2"},
86                new String[] {"sys.app3", "sys.app4", "sys.app5"});
87        addSystemAppsForIntent(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER),
88                "sys.app1", "sys.app4", "sys.app6");
89        addSystemAppsForIntent(new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE),
90                "sys.app2", "sys.app5", "sys.app7");
91        addDownloadedApps("app1", "app2");
92        when(mPm.getInstalledApplications(anyInt())).thenReturn(mInstalledAppInfos);
93
94        mHelper.fetchAndMergeApps();
95
96        final ArrayList<String> notExpectedInVisibleApps = new ArrayList<>();
97        // System apps that require an account and doesn't see restricted account are
98        // not part of visibleApps.
99        notExpectedInVisibleApps.add("sys.app0");
100        // Default system IMEs are not part of visibleApps.
101        notExpectedInVisibleApps.add("sys.app1");
102        notExpectedInVisibleApps.add("sys.app2");
103
104        final ArrayList<String> expectedInVisibleApps = new ArrayList<>();
105        expectedInVisibleApps.add("sys.app4");
106        expectedInVisibleApps.add("sys.app5");
107        expectedInVisibleApps.add("sys.app6");
108        expectedInVisibleApps.add("sys.app7");
109        expectedInVisibleApps.add("app1");
110        expectedInVisibleApps.add("app2");
111
112        for (AppRestrictionsHelper.SelectableAppInfo info : mHelper.getVisibleApps()) {
113            if (expectedInVisibleApps.contains(info.packageName)) {
114                expectedInVisibleApps.remove(info.packageName);
115            } else if (notExpectedInVisibleApps.contains(info.packageName)) {
116                fail("Package: " + info.packageName + " should not be included in visibleApps");
117            } else {
118                fail("Unknown package: " + info.packageName);
119            }
120        }
121        assertEquals("Some expected apps are not inclued in visibleApps: " + expectedInVisibleApps,
122                0, expectedInVisibleApps.size());
123
124        assertFalse("System apps that require an account and doesn't see restricted account "
125                + "should be marked for removal", mHelper.isPackageSelected("sys.app0"));
126    }
127
128    public void testApplyUserAppsStates() throws Exception {
129        final int testUserId = mTestUser.getIdentifier();
130        mHelper.setPackageSelected("app1", true);
131
132        mHelper.setPackageSelected("app2", true);
133        ApplicationInfo info = new ApplicationInfo();
134        info.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
135        info.flags |= ApplicationInfo.FLAG_INSTALLED;
136        when(mIpm.getApplicationInfo(eq("app2"), anyInt(), eq(testUserId)))
137                .thenReturn(info);
138
139        mHelper.setPackageSelected("app3", false);
140        info = new ApplicationInfo();
141        when(mIpm.getApplicationInfo(eq("app3"), anyInt(), eq(testUserId)))
142                .thenReturn(info);
143
144        AppRestrictionsHelper.OnDisableUiForPackageListener mockListener =
145                mock(AppRestrictionsHelper.OnDisableUiForPackageListener.class);
146        mHelper.applyUserAppsStates(mockListener);
147
148        verify(mIpm, times(1)).installExistingPackageAsUser("app1", testUserId,
149                0 /*installFlags*/, PackageManager.INSTALL_REASON_UNKNOWN);
150        verify(mIpm, times(1)).setApplicationHiddenSettingAsUser("app2", false, testUserId);
151        verify(mockListener).onDisableUiForPackage("app2");
152        verify(mPm, times(1)).deletePackageAsUser(eq("app3"),
153                nullable(IPackageDeleteObserver.class), anyInt(), eq(mTestUser.getIdentifier()));
154    }
155
156    private void addsystemImes(String[] defaultImes, String[] otherImes) throws
157            PackageManager.NameNotFoundException, RemoteException {
158        final ArrayList<InputMethodInfo> inputMethods = new ArrayList<>();
159        for (String pkg : defaultImes) {
160            final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
161            final InputMethodInfo inputMethodInfo = new InputMethodInfo(
162                    ri, false, null, null, 0, true, true, false);
163            inputMethods.add(inputMethodInfo);
164            addInstalledApp(ri);
165        }
166        for (String pkg : otherImes) {
167            final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
168            final InputMethodInfo inputMethodInfo = new InputMethodInfo(
169                    ri, false, null, null, 0, false, true, false);
170            inputMethods.add(inputMethodInfo);
171            addInstalledApp(ri);
172        }
173
174        mInjector.setInputMethodList(inputMethods);
175    }
176
177    private void addSystemAppsForIntent(Intent intent, String... packages) throws Exception {
178        List<ResolveInfo> resolveInfos = new ArrayList<>();
179        for (String pkg : packages) {
180            final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
181            resolveInfos.add(ri);
182            addInstalledApp(ri);
183        }
184        when(mPm.queryIntentActivities(argThat(new IntentMatcher(intent)), anyInt()))
185                .thenReturn(resolveInfos);
186    }
187
188    private void addSystemAppsWithRequiredAccounts(String... packages) throws Exception {
189        for (String pkg : packages) {
190            final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
191            final PackageInfo packageInfo = new PackageInfo();
192            packageInfo.applicationInfo = ri.activityInfo.applicationInfo;
193            packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
194            packageInfo.requiredAccountType = "account";
195            packageInfo.restrictedAccountType = null;
196            mInstalledAppInfos.add(packageInfo.applicationInfo);
197            when(mPm.getPackageInfo(eq(pkg), anyInt())).thenReturn(packageInfo);
198        }
199    }
200
201    private void addDownloadedApps(String... packages) throws Exception {
202        for (String pkg : packages) {
203            final ResolveInfo ri = createResolveInfo(pkg);
204            addInstalledApp(ri);
205        }
206    }
207
208    private void addInstalledApp(ResolveInfo ri) throws PackageManager.NameNotFoundException {
209        final String pkgName = ri.activityInfo.packageName;
210        if (mInstalledApps.contains(pkgName)) {
211            return;
212        }
213        final PackageInfo packageInfo = new PackageInfo();
214        packageInfo.applicationInfo = ri.activityInfo.applicationInfo;
215        packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
216        mInstalledAppInfos.add(packageInfo.applicationInfo);
217        when(mPm.getPackageInfo(eq(pkgName), anyInt())).thenReturn(packageInfo);
218    }
219
220    private ResolveInfo createResolveInfoForSystemApp(String packageName) {
221        final ResolveInfo ri = createResolveInfo(packageName);
222        ri.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
223        ri.serviceInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
224        return ri;
225    }
226
227    private ResolveInfo createResolveInfo(String packageName) {
228        final ResolveInfo ri = new ResolveInfo();
229        final ApplicationInfo applicationInfo = new ApplicationInfo();
230        applicationInfo.packageName = packageName;
231        final ActivityInfo activityInfo = new ActivityInfo();
232        activityInfo.applicationInfo = applicationInfo;
233        activityInfo.packageName = packageName;
234        activityInfo.name = "";
235        ri.activityInfo = activityInfo;
236        final ServiceInfo serviceInfo = new ServiceInfo();
237        serviceInfo.applicationInfo = applicationInfo;
238        serviceInfo.packageName = packageName;
239        serviceInfo.name = "";
240        ri.serviceInfo = serviceInfo;
241        return ri;
242    }
243
244    private class IntentMatcher implements ArgumentMatcher<Intent> {
245        private final Intent mIntent;
246
247        IntentMatcher(Intent intent) {
248            mIntent = intent;
249        }
250
251        @Override
252        public boolean matches(Intent argument) {
253            return argument != null && argument.filterEquals(mIntent);
254        }
255
256        @Override
257        public String toString() {
258            return "Expected: " + mIntent;
259        }
260    }
261
262    private class TestInjector extends AppRestrictionsHelper.Injector {
263        List<InputMethodInfo> mImis;
264
265        TestInjector() {
266            super(mContext, mTestUser);
267        }
268
269        @Override
270        Context getContext() {
271            return mContext;
272        }
273
274        @Override
275        UserHandle getUser() {
276            return mTestUser;
277        }
278
279        @Override
280        PackageManager getPackageManager() {
281            return mPm;
282        }
283
284        @Override
285        IPackageManager getIPackageManager() {
286            return mIpm;
287        }
288
289        @Override
290        UserManager getUserManager() {
291            return mUm;
292        }
293
294        @Override
295        List<InputMethodInfo> getInputMethodList() {
296            return mImis;
297        }
298
299        void setInputMethodList(List<InputMethodInfo> imis) {
300            mImis = imis;
301        }
302    }
303}
304