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 
17 package com.android.providers.contacts.enterprise;
18 
19 import android.annotation.NonNull;
20 import android.app.admin.DevicePolicyManager;
21 import android.content.Context;
22 import android.net.Uri;
23 import android.os.UserHandle;
24 import android.provider.ContactsContract;
25 import android.provider.ContactsContract.Directory;
26 import android.provider.Settings;
27 import android.util.Log;
28 
29 import com.android.providers.contacts.ContactsProvider2;
30 import com.android.providers.contacts.ProfileAwareUriMatcher;
31 import com.android.providers.contacts.util.UserUtils;
32 import com.google.common.annotations.VisibleForTesting;
33 
34 import static android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH;
35 
36 /**
37  * Digest contacts policy from DevcicePolicyManager and guard enterprise uri
38  */
39 public class EnterprisePolicyGuard {
40     private static final boolean VERBOSE_LOGGING = ContactsProvider2.VERBOSE_LOGGING;
41     private static final String TAG = ContactsProvider2.TAG;
42 
43     private static final ProfileAwareUriMatcher sUriMatcher = ContactsProvider2.sUriMatcher;
44 
45     final private Context mContext;
46     final private DevicePolicyManager mDpm;
47 
EnterprisePolicyGuard(Context context)48     public EnterprisePolicyGuard(Context context) {
49         mContext = context;
50         mDpm = context.getSystemService(DevicePolicyManager.class);
51     }
52 
53     /**
54      * Check if cross profile query is allowed for the given uri
55      *
56      * @param uri Uri that we want to check.
57      * @return True if cross profile query is allowed for this uri
58      */
isCrossProfileAllowed(@onNull Uri uri)59     public boolean isCrossProfileAllowed(@NonNull Uri uri) {
60         final int uriCode = sUriMatcher.match(uri);
61         final UserHandle currentHandle = new UserHandle(UserUtils.getCurrentUserHandle(mContext));
62         if (uriCode == -1 || currentHandle == null) {
63             return false;
64         }
65 
66         if (isUriWhitelisted(uriCode)) {
67             return true;
68         }
69 
70         final boolean isCallerIdEnabled = !mDpm.getCrossProfileCallerIdDisabled(currentHandle);
71         final boolean isContactsSearchPolicyEnabled =
72                 !mDpm.getCrossProfileContactsSearchDisabled(currentHandle);
73         final boolean isBluetoothContactSharingEnabled =
74                 !mDpm.getBluetoothContactSharingDisabled(currentHandle);
75         final boolean isContactRemoteSearchUserEnabled = isContactRemoteSearchUserSettingEnabled();
76 
77         final String directory = uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
78 
79         if (VERBOSE_LOGGING) {
80             Log.v(TAG, "isCallerIdEnabled: " + isCallerIdEnabled);
81             Log.v(TAG, "isContactsSearchPolicyEnabled: " + isContactsSearchPolicyEnabled);
82             Log.v(TAG, "isBluetoothContactSharingEnabled: " + isBluetoothContactSharingEnabled);
83             Log.v(TAG, "isContactRemoteSearchUserEnabled: " + isContactRemoteSearchUserEnabled);
84         }
85 
86         // If it is a remote directory, it is allowed only when
87         // (i) The uri supports directory
88         // (ii) User enables it in settings
89         if (directory != null) {
90             final long directoryId = Long.parseLong(directory);
91             if (Directory.isRemoteDirectoryId(directoryId)
92                     && !(isCrossProfileDirectorySupported(uri)
93                     && isContactRemoteSearchUserEnabled)) {
94                 return false;
95             }
96         }
97 
98         // If either guard policy allows access, return true.
99         return (isCallerIdGuarded(uriCode) && isCallerIdEnabled)
100                 || (isContactsSearchGuarded(uriCode) && isContactsSearchPolicyEnabled)
101                 || (isBluetoothContactSharing(uriCode) && isBluetoothContactSharingEnabled);
102     }
103 
isUriWhitelisted(int uriCode)104     private boolean isUriWhitelisted(int uriCode) {
105         switch (uriCode) {
106             case ContactsProvider2.PROFILE_AS_VCARD:
107             case ContactsProvider2.CONTACTS_AS_VCARD:
108             case ContactsProvider2.CONTACTS_AS_MULTI_VCARD:
109                 return true;
110             default:
111                 return false;
112         }
113     }
114 
115     /**
116      * Check if uri is a cross profile query with directory param supported.
117      *
118      * @param uri Uri that we want to check.
119      * @return True if it is an enterprise uri.
120      */
121     @VisibleForTesting
isCrossProfileDirectorySupported(@onNull Uri uri)122     protected boolean isCrossProfileDirectorySupported(@NonNull Uri uri) {
123         final int uriCode = sUriMatcher.match(uri);
124         return isDirectorySupported(uriCode);
125     }
126 
isValidEnterpriseUri(@onNull Uri uri)127     public boolean isValidEnterpriseUri(@NonNull Uri uri) {
128         final int uriCode = sUriMatcher.match(uri);
129         return isValidEnterpriseUri(uriCode);
130     }
131 
isDirectorySupported(int uriCode)132     private static boolean isDirectorySupported(int uriCode) {
133         switch(uriCode) {
134             case ContactsProvider2.PHONE_LOOKUP:
135             case ContactsProvider2.EMAILS_LOOKUP:
136             case ContactsProvider2.CONTACTS_FILTER:
137             case ContactsProvider2.PHONES_FILTER:
138             case ContactsProvider2.CALLABLES_FILTER:
139             case ContactsProvider2.EMAILS_FILTER:
140             case ContactsProvider2.DIRECTORY_FILE_ENTERPRISE:
141                 return true;
142             default:
143                 return false;
144         }
145     }
146 
isValidEnterpriseUri(int uriCode)147     private static boolean isValidEnterpriseUri(int uriCode) {
148         switch (uriCode) {
149             case ContactsProvider2.PHONE_LOOKUP_ENTERPRISE:
150             case ContactsProvider2.EMAILS_LOOKUP_ENTERPRISE:
151             case ContactsProvider2.CONTACTS_FILTER_ENTERPRISE:
152             case ContactsProvider2.PHONES_FILTER_ENTERPRISE:
153             case ContactsProvider2.CALLABLES_FILTER_ENTERPRISE:
154             case ContactsProvider2.EMAILS_FILTER_ENTERPRISE:
155             case ContactsProvider2.DIRECTORIES_ENTERPRISE:
156             case ContactsProvider2.DIRECTORIES_ID_ENTERPRISE:
157             case ContactsProvider2.DIRECTORY_FILE_ENTERPRISE:
158                 return true;
159             default:
160                 return false;
161         }
162     }
163 
isCallerIdGuarded(int uriCode)164     private static boolean isCallerIdGuarded(int uriCode) {
165         switch(uriCode) {
166             case ContactsProvider2.DIRECTORIES:
167             case ContactsProvider2.DIRECTORIES_ID:
168             case ContactsProvider2.PHONE_LOOKUP:
169             case ContactsProvider2.EMAILS_LOOKUP:
170             case ContactsProvider2.CONTACTS_ID_PHOTO:
171             case ContactsProvider2.CONTACTS_ID_DISPLAY_PHOTO:
172             case ContactsProvider2.DIRECTORY_FILE_ENTERPRISE:
173                 return true;
174             default:
175                 return false;
176         }
177     }
178 
isContactsSearchGuarded(int uriCode)179     private static boolean isContactsSearchGuarded(int uriCode) {
180         switch(uriCode) {
181             case ContactsProvider2.DIRECTORIES:
182             case ContactsProvider2.DIRECTORIES_ID:
183             case ContactsProvider2.CONTACTS_FILTER:
184             case ContactsProvider2.CALLABLES_FILTER:
185             case ContactsProvider2.PHONES_FILTER:
186             case ContactsProvider2.EMAILS_FILTER:
187             case ContactsProvider2.CONTACTS_ID_PHOTO:
188             case ContactsProvider2.CONTACTS_ID_DISPLAY_PHOTO:
189             case ContactsProvider2.DIRECTORY_FILE_ENTERPRISE:
190                 return true;
191             default:
192                 return false;
193         }
194     }
195 
isBluetoothContactSharing(int uriCode)196     private static boolean isBluetoothContactSharing(int uriCode) {
197         switch(uriCode) {
198             case ContactsProvider2.PHONES:
199             case ContactsProvider2.RAW_CONTACT_ENTITIES:
200                 return true;
201             default:
202                 return false;
203         }
204     }
205 
isContactRemoteSearchUserSettingEnabled()206     protected boolean isContactRemoteSearchUserSettingEnabled() {
207         return Settings.Secure.getInt(
208                 mContext.getContentResolver(),
209                 MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0) == 1;
210     }
211 }
212