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 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 */ 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 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 122 protected boolean isCrossProfileDirectorySupported(@NonNull Uri uri) { 123 final int uriCode = sUriMatcher.match(uri); 124 return isDirectorySupported(uriCode); 125 } 126 127 public boolean isValidEnterpriseUri(@NonNull Uri uri) { 128 final int uriCode = sUriMatcher.match(uri); 129 return isValidEnterpriseUri(uriCode); 130 } 131 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 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 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 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 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 206 protected boolean isContactRemoteSearchUserSettingEnabled() { 207 return Settings.Secure.getInt( 208 mContext.getContentResolver(), 209 MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0) == 1; 210 } 211 } 212