blob: 46841050475ca4a56e363dfa65c323d91fd3fbcb [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.providers.contacts.enterprise;
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.net.Uri;
import android.os.UserHandle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Directory;
import android.provider.Settings;
import android.util.Log;
import com.android.providers.contacts.ContactsProvider2;
import com.android.providers.contacts.ProfileAwareUriMatcher;
import com.android.providers.contacts.util.UserUtils;
import com.google.common.annotations.VisibleForTesting;
import static android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH;
/**
* Digest contacts policy from DevcicePolicyManager and guard enterprise uri
*/
public class EnterprisePolicyGuard {
private static final boolean VERBOSE_LOGGING = ContactsProvider2.VERBOSE_LOGGING;
private static final String TAG = ContactsProvider2.TAG;
private static final ProfileAwareUriMatcher sUriMatcher = ContactsProvider2.sUriMatcher;
final private Context mContext;
final private DevicePolicyManager mDpm;
public EnterprisePolicyGuard(Context context) {
mContext = context;
mDpm = context.getSystemService(DevicePolicyManager.class);
}
/**
* Check if cross profile query is allowed for the given uri
*
* @param uri Uri that we want to check.
* @return True if cross profile query is allowed for this uri
*/
public boolean isCrossProfileAllowed(@NonNull Uri uri) {
final int uriCode = sUriMatcher.match(uri);
final UserHandle currentHandle = new UserHandle(UserUtils.getCurrentUserHandle(mContext));
if (uriCode == -1 || currentHandle == null) {
return false;
}
if (isUriWhitelisted(uriCode)) {
return true;
}
final boolean isCallerIdEnabled = !mDpm.getCrossProfileCallerIdDisabled(currentHandle);
final boolean isContactsSearchPolicyEnabled =
!mDpm.getCrossProfileContactsSearchDisabled(currentHandle);
final boolean isBluetoothContactSharingEnabled =
!mDpm.getBluetoothContactSharingDisabled(currentHandle);
final boolean isContactRemoteSearchUserEnabled = isContactRemoteSearchUserSettingEnabled();
final String directory = uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
if (VERBOSE_LOGGING) {
Log.v(TAG, "isCallerIdEnabled: " + isCallerIdEnabled);
Log.v(TAG, "isContactsSearchPolicyEnabled: " + isContactsSearchPolicyEnabled);
Log.v(TAG, "isBluetoothContactSharingEnabled: " + isBluetoothContactSharingEnabled);
Log.v(TAG, "isContactRemoteSearchUserEnabled: " + isContactRemoteSearchUserEnabled);
}
// If it is a remote directory, it is allowed only when
// (i) The uri supports directory
// (ii) User enables it in settings
if (directory != null) {
final long directoryId = Long.parseLong(directory);
if (Directory.isRemoteDirectoryId(directoryId)
&& !(isCrossProfileDirectorySupported(uri)
&& isContactRemoteSearchUserEnabled)) {
return false;
}
}
// If either guard policy allows access, return true.
return (isCallerIdGuarded(uriCode) && isCallerIdEnabled)
|| (isContactsSearchGuarded(uriCode) && isContactsSearchPolicyEnabled)
|| (isBluetoothContactSharing(uriCode) && isBluetoothContactSharingEnabled);
}
private boolean isUriWhitelisted(int uriCode) {
switch (uriCode) {
case ContactsProvider2.PROFILE_AS_VCARD:
case ContactsProvider2.CONTACTS_AS_VCARD:
case ContactsProvider2.CONTACTS_AS_MULTI_VCARD:
return true;
default:
return false;
}
}
/**
* Check if uri is a cross profile query with directory param supported.
*
* @param uri Uri that we want to check.
* @return True if it is an enterprise uri.
*/
@VisibleForTesting
protected boolean isCrossProfileDirectorySupported(@NonNull Uri uri) {
final int uriCode = sUriMatcher.match(uri);
return isDirectorySupported(uriCode);
}
public boolean isValidEnterpriseUri(@NonNull Uri uri) {
final int uriCode = sUriMatcher.match(uri);
return isValidEnterpriseUri(uriCode);
}
private static boolean isDirectorySupported(int uriCode) {
switch(uriCode) {
case ContactsProvider2.PHONE_LOOKUP:
case ContactsProvider2.EMAILS_LOOKUP:
case ContactsProvider2.CONTACTS_FILTER:
case ContactsProvider2.PHONES_FILTER:
case ContactsProvider2.CALLABLES_FILTER:
case ContactsProvider2.EMAILS_FILTER:
case ContactsProvider2.DIRECTORY_FILE_ENTERPRISE:
return true;
default:
return false;
}
}
private static boolean isValidEnterpriseUri(int uriCode) {
switch (uriCode) {
case ContactsProvider2.PHONE_LOOKUP_ENTERPRISE:
case ContactsProvider2.EMAILS_LOOKUP_ENTERPRISE:
case ContactsProvider2.CONTACTS_FILTER_ENTERPRISE:
case ContactsProvider2.PHONES_FILTER_ENTERPRISE:
case ContactsProvider2.CALLABLES_FILTER_ENTERPRISE:
case ContactsProvider2.EMAILS_FILTER_ENTERPRISE:
case ContactsProvider2.DIRECTORIES_ENTERPRISE:
case ContactsProvider2.DIRECTORIES_ID_ENTERPRISE:
case ContactsProvider2.DIRECTORY_FILE_ENTERPRISE:
return true;
default:
return false;
}
}
private static boolean isCallerIdGuarded(int uriCode) {
switch(uriCode) {
case ContactsProvider2.DIRECTORIES:
case ContactsProvider2.DIRECTORIES_ID:
case ContactsProvider2.PHONE_LOOKUP:
case ContactsProvider2.EMAILS_LOOKUP:
case ContactsProvider2.CONTACTS_ID_PHOTO:
case ContactsProvider2.CONTACTS_ID_DISPLAY_PHOTO:
case ContactsProvider2.DIRECTORY_FILE_ENTERPRISE:
return true;
default:
return false;
}
}
private static boolean isContactsSearchGuarded(int uriCode) {
switch(uriCode) {
case ContactsProvider2.DIRECTORIES:
case ContactsProvider2.DIRECTORIES_ID:
case ContactsProvider2.CONTACTS_FILTER:
case ContactsProvider2.CALLABLES_FILTER:
case ContactsProvider2.PHONES_FILTER:
case ContactsProvider2.EMAILS_FILTER:
case ContactsProvider2.CONTACTS_ID_PHOTO:
case ContactsProvider2.CONTACTS_ID_DISPLAY_PHOTO:
case ContactsProvider2.DIRECTORY_FILE_ENTERPRISE:
return true;
default:
return false;
}
}
private static boolean isBluetoothContactSharing(int uriCode) {
switch(uriCode) {
case ContactsProvider2.PHONES:
case ContactsProvider2.RAW_CONTACT_ENTITIES:
return true;
default:
return false;
}
}
protected boolean isContactRemoteSearchUserSettingEnabled() {
return Settings.Secure.getInt(
mContext.getContentResolver(),
MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, 0) == 1;
}
}