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 */
16package com.android.providers.contacts.enterprise;
17
18import android.app.admin.DevicePolicyManager;
19import android.content.ContentResolver;
20import android.content.Context;
21import android.content.pm.UserInfo;
22import android.net.Uri;
23import android.os.UserHandle;
24import android.os.UserManager;
25import android.provider.ContactsContract;
26import android.provider.ContactsContract.Directory;
27import android.test.AndroidTestCase;
28import android.test.mock.MockContext;
29import android.test.suitebuilder.annotation.SmallTest;
30
31import org.mockito.Matchers;
32
33import java.util.Arrays;
34import java.util.List;
35
36import static org.mockito.Mockito.mock;
37import static org.mockito.Mockito.when;
38
39
40/**
41 * Unit tests for {@link EnterprisePolicyGuard}.
42 */
43@SmallTest
44public class EnterprisePolicyGuardTest extends AndroidTestCase {
45    private static final String SYSTEM_PROPERTY_DEXMAKER_DEXCACHE = "dexmaker.dexcache";
46
47    private static final int CONTACT_ID = 10;
48    private static final String CONTACT_NAME = "david";
49    private static final String CONTACT_EMAIL = "david.green@android.com";
50    private static final String CONTACT_PHONE = "+1234567890";
51    private static final long DIRECTORY_ID = Directory.ENTERPRISE_DEFAULT;
52
53    private static final Uri URI_CONTACTS_ID_PHOTO =
54            Uri.parse("content://com.android.contacts/contacts/" + CONTACT_ID + "/photo");
55    private static final Uri URI_CONTACTS_ID_DISPLAY_PHOTO = Uri
56            .parse("content://com.android.contacts/contacts/" + CONTACT_ID + "/display_photo");
57    private static final Uri URI_CONTACTS_FILTER =
58            Uri.parse("content://com.android.contacts/contacts/filter/" + CONTACT_NAME);
59    private static final Uri URI_PHONES_FILTER = Uri
60            .parse("content://com.android.contacts/data/phones/filter/" + CONTACT_NAME);
61    private static final Uri URI_CALLABLES_FILTER = Uri.parse(
62            "content://com.android.contacts/data/callables/filter/" + CONTACT_NAME);
63    private static final Uri URI_EMAILS_FILTER = Uri
64            .parse("content://com.android.contacts/data/emails/filter/" + CONTACT_NAME);
65    private static final Uri URI_EMAILS_LOOKUP =
66            Uri.parse("content://com.android.contacts/data/emails/lookup/"
67                    + Uri.encode(CONTACT_EMAIL));
68    private static final Uri URI_PHONE_LOOKUP = Uri.parse(
69            "content://com.android.contacts/phone_lookup/" + Uri.encode(CONTACT_PHONE));
70    private static final Uri URI_DIRECTORIES =
71            Uri.parse("content://com.android.contacts/directories");
72    private static final Uri URI_DIRECTORIES_ID =
73            Uri.parse("content://com.android.contacts/directories/" + DIRECTORY_ID);
74    private static final Uri URI_DIRECTORY_FILE =
75            Uri.parse("content://com.android.contacts/directory_file_enterprise/content%3A%2F%2F"
76                    + "com.google.contacts.gal.provider%2Fphoto%2F?directory=1000000002");
77
78    private static final Uri URI_OTHER =
79            Uri.parse("content://com.android.contacts/contacts/" + CONTACT_ID);
80
81    // Please notice that the directory id should be < ENTERPRISE_BASE because the id should be
82    // substracted before passing to enterprise side.
83    private static final int REMOTE_DIRECTORY_ID = 10;
84
85    @Override
86    protected void setUp() throws Exception {
87        super.setUp();
88        System.setProperty(SYSTEM_PROPERTY_DEXMAKER_DEXCACHE, getContext().getCacheDir().getPath());
89        Thread.currentThread().setContextClassLoader(EnterprisePolicyGuard.class.getClassLoader());
90    }
91
92    @Override
93    protected void tearDown() throws Exception {
94        super.tearDown();
95        System.clearProperty(SYSTEM_PROPERTY_DEXMAKER_DEXCACHE);
96    }
97
98    public void testDirectorySupport() {
99        EnterprisePolicyGuard guard = new EnterprisePolicyGuardTestable(getContext(), true);
100        checkDirectorySupport(guard, URI_PHONE_LOOKUP, true);
101        checkDirectorySupport(guard, URI_EMAILS_LOOKUP, true);
102        checkDirectorySupport(guard, URI_CONTACTS_FILTER, true);
103        checkDirectorySupport(guard, URI_PHONES_FILTER, true);
104        checkDirectorySupport(guard, URI_CALLABLES_FILTER, true);
105        checkDirectorySupport(guard, URI_EMAILS_FILTER, true);
106        checkDirectorySupport(guard, URI_DIRECTORY_FILE, true);
107        checkDirectorySupport(guard, URI_DIRECTORIES, false);
108        checkDirectorySupport(guard, URI_DIRECTORIES_ID, false);
109        checkDirectorySupport(guard, URI_CONTACTS_ID_PHOTO, false);
110        checkDirectorySupport(guard, URI_CONTACTS_ID_DISPLAY_PHOTO, false);
111        checkDirectorySupport(guard, URI_OTHER, false);
112    }
113
114
115    public void testCrossProfile_userSettingOn() {
116        Context context;
117        EnterprisePolicyGuard guard;
118        // All enabled.
119        context = getMockContext(true, true);
120        guard = new EnterprisePolicyGuardTestable(context, true);
121        checkCrossProfile(guard, URI_PHONE_LOOKUP, true);
122        checkCrossProfile(guard, URI_EMAILS_LOOKUP, true);
123        checkCrossProfile(guard, URI_CONTACTS_FILTER, true);
124        checkCrossProfile(guard, URI_PHONES_FILTER, true);
125        checkCrossProfile(guard, URI_CALLABLES_FILTER, true);
126        checkCrossProfile(guard, URI_EMAILS_FILTER, true);
127        checkCrossProfile(guard, URI_DIRECTORY_FILE, true);
128        checkCrossProfile(guard, URI_DIRECTORIES, true);
129        checkCrossProfile(guard, URI_DIRECTORIES_ID, true);
130        checkCrossProfile(guard, URI_CONTACTS_ID_PHOTO, true);
131        checkCrossProfile(guard, URI_CONTACTS_ID_DISPLAY_PHOTO, true);
132        checkCrossProfile(guard, URI_OTHER, false);
133
134        // Only ContactsSearch is disabled
135        context = getMockContext(true, /* isContactsSearchEnabled= */ false);
136        guard = new EnterprisePolicyGuardTestable(context, true);
137        checkCrossProfile(guard, URI_PHONE_LOOKUP, true);
138        checkCrossProfile(guard, URI_EMAILS_LOOKUP, true);
139        checkCrossProfile(guard, URI_CONTACTS_FILTER, false);
140        checkCrossProfile(guard, URI_PHONES_FILTER, false);
141        checkCrossProfile(guard, URI_CALLABLES_FILTER, false);
142        checkCrossProfile(guard, URI_EMAILS_FILTER, false);
143        checkCrossProfile(guard, URI_DIRECTORY_FILE, true);
144        checkCrossProfile(guard, URI_DIRECTORIES, true);
145        checkCrossProfile(guard, URI_DIRECTORIES_ID, true);
146        checkCrossProfile(guard, URI_CONTACTS_ID_PHOTO, true);
147        checkCrossProfile(guard, URI_CONTACTS_ID_DISPLAY_PHOTO, true);
148        checkCrossProfile(guard, URI_OTHER, false);
149
150        // Only CallerId is disabled
151        context = getMockContext(/* isCallerIdEnabled= */ false, true);
152        guard = new EnterprisePolicyGuardTestable(context, true);
153        checkCrossProfile(guard, URI_PHONE_LOOKUP, false);
154        checkCrossProfile(guard, URI_EMAILS_LOOKUP, false);
155        checkCrossProfile(guard, URI_CONTACTS_FILTER, true);
156        checkCrossProfile(guard, URI_PHONES_FILTER, true);
157        checkCrossProfile(guard, URI_CALLABLES_FILTER, true);
158        checkCrossProfile(guard, URI_EMAILS_FILTER, true);
159        checkCrossProfile(guard, URI_DIRECTORY_FILE, true);
160        checkCrossProfile(guard, URI_DIRECTORIES, true);
161        checkCrossProfile(guard, URI_DIRECTORIES_ID, true);
162        checkCrossProfile(guard, URI_CONTACTS_ID_PHOTO, true);
163        checkCrossProfile(guard, URI_CONTACTS_ID_DISPLAY_PHOTO, true);
164        checkCrossProfile(guard, URI_OTHER, false);
165
166        // CallerId and ContactsSearch are disabled
167        context = getMockContext(/* isCallerIdEnabled= */ false,
168            /* isContactsSearchEnabled= */ false);
169        guard = new EnterprisePolicyGuardTestable(context, true);
170        checkCrossProfile(guard, URI_PHONE_LOOKUP, false);
171        checkCrossProfile(guard, URI_EMAILS_LOOKUP, false);
172        checkCrossProfile(guard, URI_CONTACTS_FILTER, false);
173        checkCrossProfile(guard, URI_PHONES_FILTER, false);
174        checkCrossProfile(guard, URI_CALLABLES_FILTER, false);
175        checkCrossProfile(guard, URI_EMAILS_FILTER, false);
176        checkCrossProfile(guard, URI_DIRECTORY_FILE, false);
177        checkCrossProfile(guard, URI_DIRECTORIES, false);
178        checkCrossProfile(guard, URI_DIRECTORIES_ID, false);
179        checkCrossProfile(guard, URI_CONTACTS_ID_PHOTO, false);
180        checkCrossProfile(guard, URI_CONTACTS_ID_DISPLAY_PHOTO, false);
181        checkCrossProfile(guard, URI_OTHER, false);
182    }
183
184    public void testCrossProfile_userSettingOff() {
185        // All enabled.
186        Context context = getMockContext(true, true);
187        EnterprisePolicyGuard guard = new EnterprisePolicyGuardTestable(context, false);
188        // All directory supported Uris with remote directory id should not allowed.
189        checkCrossProfile(guard, appendRemoteDirectoryId(URI_PHONE_LOOKUP), false);
190        checkCrossProfile(guard, appendRemoteDirectoryId(URI_EMAILS_LOOKUP), false);
191        checkCrossProfile(guard, appendRemoteDirectoryId(URI_CONTACTS_FILTER), false);
192        checkCrossProfile(guard, appendRemoteDirectoryId(URI_PHONES_FILTER), false);
193        checkCrossProfile(guard, appendRemoteDirectoryId(URI_CALLABLES_FILTER), false);
194        checkCrossProfile(guard, appendRemoteDirectoryId(URI_EMAILS_FILTER), false);
195        checkCrossProfile(guard, URI_DIRECTORY_FILE, false);
196
197        // Always allow uri with no directory support.
198        checkCrossProfile(guard, URI_DIRECTORIES, true);
199        checkCrossProfile(guard, URI_DIRECTORIES_ID, true);
200        checkCrossProfile(guard, URI_CONTACTS_ID_PHOTO, true);
201        checkCrossProfile(guard, URI_CONTACTS_ID_DISPLAY_PHOTO, true);
202        checkCrossProfile(guard, URI_OTHER, false);
203
204        // Always allow uri with no remote directory id.
205        checkCrossProfile(guard, URI_PHONE_LOOKUP, true);
206        checkCrossProfile(guard, URI_EMAILS_LOOKUP, true);
207        checkCrossProfile(guard, URI_CONTACTS_FILTER, true);
208        checkCrossProfile(guard, URI_PHONES_FILTER, true);
209        checkCrossProfile(guard, URI_CALLABLES_FILTER, true);
210        checkCrossProfile(guard, URI_EMAILS_FILTER, true);
211    }
212
213    private static Uri appendRemoteDirectoryId(Uri uri) {
214        return appendDirectoryId(uri, REMOTE_DIRECTORY_ID);
215    }
216
217    private static Uri appendDirectoryId(Uri uri, int directoryId) {
218        return uri.buildUpon().appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
219                String.valueOf(directoryId)).build();
220    }
221
222
223    private static void checkDirectorySupport(EnterprisePolicyGuard guard, Uri uri,
224            boolean expected) {
225        if (expected) {
226            assertTrue("Expected true but got false for uri: " + uri,
227                    guard.isCrossProfileDirectorySupported(uri));
228        } else {
229            assertFalse("Expected false but got true for uri: " + uri,
230                    guard.isCrossProfileDirectorySupported(uri));
231        }
232    }
233
234    private static void checkCrossProfile(EnterprisePolicyGuard guard, Uri uri, boolean expected) {
235        if (expected) {
236            assertTrue("Expected true but got false for uri: " + uri,
237                    guard.isCrossProfileAllowed(uri));
238        } else {
239            assertFalse("Expected false but got true for uri: " + uri,
240                    guard.isCrossProfileAllowed(uri));
241        }
242    }
243
244    private static final int CURRENT_USER_ID = 0;
245    private static final int WORK_USER_ID = 10;
246    private static final UserInfo CURRENT_USER_INFO =
247            new UserInfo(CURRENT_USER_ID, "user0", CURRENT_USER_ID);
248    private static final UserInfo WORK_USER_INFO =
249            new UserInfo(WORK_USER_ID, "user10", UserInfo.FLAG_MANAGED_PROFILE);
250
251    private static final List<UserInfo> NORMAL_USERINFO_LIST =
252            Arrays.asList(new UserInfo[] {CURRENT_USER_INFO});
253
254    private static final List<UserInfo> MANAGED_USERINFO_LIST =
255            Arrays.asList(new UserInfo[] {CURRENT_USER_INFO, WORK_USER_INFO});
256
257
258    private Context getMockContext(boolean isCallerIdEnabled, boolean isContactsSearchEnabled) {
259        DevicePolicyManager mockDpm = mock(DevicePolicyManager.class);
260        when(mockDpm.getCrossProfileCallerIdDisabled(Matchers.<UserHandle>any()))
261                .thenReturn(!isCallerIdEnabled);
262        when(mockDpm.getCrossProfileContactsSearchDisabled(Matchers.<UserHandle>any()))
263                .thenReturn(!isContactsSearchEnabled);
264
265        List<UserInfo> userInfos = MANAGED_USERINFO_LIST;
266        UserManager mockUm = mock(UserManager.class);
267        when(mockUm.getUserHandle()).thenReturn(CURRENT_USER_ID);
268        when(mockUm.getUsers()).thenReturn(userInfos);
269        when(mockUm.getProfiles(Matchers.anyInt())).thenReturn(userInfos);
270        when(mockUm.getProfileParent(WORK_USER_ID)).thenReturn(CURRENT_USER_INFO);
271
272        Context mockContext = new TestMockContext(getContext(), mockDpm, mockUm);
273
274        return mockContext;
275    }
276
277    private static final class TestMockContext extends MockContext {
278        private Context mRealContext;
279        private DevicePolicyManager mDpm;
280        private UserManager mUm;
281
282        public TestMockContext(Context realContext, DevicePolicyManager dpm, UserManager um) {
283            mRealContext = realContext;
284            mDpm = dpm;
285            mUm = um;
286        }
287
288        public Object getSystemService(String name) {
289            if (Context.DEVICE_POLICY_SERVICE.equals(name)) {
290                return mDpm;
291            } else if (Context.USER_SERVICE.equals(name)) {
292                return mUm;
293            } else {
294                return super.getSystemService(name);
295            }
296        }
297
298        public String getSystemServiceName(Class<?> serviceClass) {
299            return mRealContext.getSystemServiceName(serviceClass);
300        }
301    }
302
303    private static class EnterprisePolicyGuardTestable extends EnterprisePolicyGuard {
304        private boolean mIsContactRemoteSearchUserSettingEnabled;
305
306        public EnterprisePolicyGuardTestable(Context context,
307                boolean isContactRemoteSearchUserSettingEnabled) {
308            super(context);
309            mIsContactRemoteSearchUserSettingEnabled = isContactRemoteSearchUserSettingEnabled;
310        }
311
312        @Override
313        protected boolean isContactRemoteSearchUserSettingEnabled() {
314            return mIsContactRemoteSearchUserSettingEnabled;
315        }
316    }
317
318}
319