blob: 9771d65d462e1b4ae992f0358962847f123dd544 [file] [log] [blame]
/*
* Copyright (C) 2018 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.server.telecom.callredirection;
import android.Manifest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.PersistableBundle;
import android.telecom.CallRedirectionService;
import android.telecom.GatewayInfo;
import android.telecom.Log;
import android.telecom.PhoneAccountHandle;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.PhoneAccountRegistrar;
import java.util.List;
public class CallRedirectionProcessorHelper {
private final Context mContext;
private final CallsManager mCallsManager;
private final PhoneAccountRegistrar mPhoneAccountRegistrar;
public CallRedirectionProcessorHelper(
Context context,
CallsManager callsManager,
PhoneAccountRegistrar phoneAccountRegistrar) {
mContext = context;
mCallsManager = callsManager;
mPhoneAccountRegistrar = phoneAccountRegistrar;
}
@VisibleForTesting
public ComponentName getUserDefinedCallRedirectionService() {
String packageName = mCallsManager.getRoleManagerAdapter().getDefaultCallRedirectionApp();
if (TextUtils.isEmpty(packageName)) {
Log.i(this, "PackageName is empty. Not performing user-defined call redirection.");
return null;
}
Intent intent = new Intent(CallRedirectionService.SERVICE_INTERFACE)
.setPackage(packageName);
return getComponentName(intent, CallRedirectionProcessor.SERVICE_TYPE_USER_DEFINED);
}
@VisibleForTesting
public ComponentName getCarrierCallRedirectionService(
PhoneAccountHandle targetPhoneAccountHandle) {
CarrierConfigManager configManager = (CarrierConfigManager)
mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (configManager == null) {
Log.i(this, "Cannot get CarrierConfigManager.");
return null;
}
PersistableBundle pb = configManager.getConfigForSubId(mPhoneAccountRegistrar
.getSubscriptionIdForPhoneAccount(targetPhoneAccountHandle));
if (pb == null) {
Log.i(this, "Cannot get PersistableBundle.");
return null;
}
String componentNameString = pb.getString(
CarrierConfigManager.KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING);
if (componentNameString == null) {
Log.i(this, "Cannot get carrier componentNameString.");
return null;
}
ComponentName componentName = ComponentName.unflattenFromString(componentNameString);
if (componentName == null) {
Log.w(this, "ComponentName is null from string: " + componentNameString);
return null;
}
Intent intent = new Intent(CallRedirectionService.SERVICE_INTERFACE);
intent.setComponent(componentName);
return getComponentName(intent, CallRedirectionProcessor.SERVICE_TYPE_CARRIER);
}
protected ComponentName getComponentName(Intent intent, String serviceType) {
List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServicesAsUser(
intent, 0, mCallsManager.getCurrentUserHandle().getIdentifier());
if (entries.isEmpty()) {
Log.i(this, "There are no " + serviceType + " call redirection services installed" +
" on this device.");
return null;
} else if (entries.size() != 1) {
Log.i(this, "There are multiple " + serviceType + " call redirection services" +
" installed on this device.");
return null;
}
ResolveInfo entry = entries.get(0);
if (entry.serviceInfo == null) {
Log.w(this, "The " + serviceType + " call redirection service has invalid" +
" service info");
return null;
}
if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals(
Manifest.permission.BIND_CALL_REDIRECTION_SERVICE)) {
Log.w(this, "CallRedirectionService must require BIND_CALL_REDIRECTION_SERVICE"
+ " permission: " + entry.serviceInfo.packageName);
return null;
}
return new ComponentName(entry.serviceInfo.packageName, entry.serviceInfo.name);
}
/**
* Format Number to E164, and remove post dial digits.
*/
protected Uri formatNumberForRedirection(Uri handle) {
return removePostDialDigits(formatNumberToE164(handle));
}
/**
* Extras the post dial digits from a given handle.
* @param handle The handle
* @return The post dial digits.
*/
public String getPostDialDigits(Uri handle) {
if (handle == null) {
return "";
}
return PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart());
}
protected Uri formatNumberToE164(Uri handle) {
String number = handle.getSchemeSpecificPart();
// Format number to E164
TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
Log.i(this, "formatNumberToE164, original number: " + Log.pii(number));
number = PhoneNumberUtils.formatNumberToE164(number, tm.getNetworkCountryIso());
Log.i(this, "formatNumberToE164, formatted E164 number: " + Log.pii(number));
// if there is a problem with parsing the phone number, formatNumberToE164 will return null;
// and should just use the original number in that case.
if (number == null) {
return handle;
} else {
return Uri.fromParts(handle.getScheme(), number, null);
}
}
protected Uri removePostDialDigits(Uri handle) {
String number = handle.getSchemeSpecificPart();
// Extract the post dial portion
number = PhoneNumberUtils.extractNetworkPortion(number);
Log.i(this, "removePostDialDigits, number after being extracted post dial digits: "
+ Log.pii(number));
// if there is a problem with parsing the phone number, removePostDialDigits will return
// null; and should just use the original number in that case.
if (number == null) {
return handle;
} else {
return Uri.fromParts(handle.getScheme(), number, null);
}
}
public static GatewayInfo getGatewayInfoFromGatewayUri(
String gatewayPackageName, Uri gatewayUri, Uri destinationUri, String postdialDigits) {
if (!TextUtils.isEmpty(gatewayPackageName) && gatewayUri != null) {
Uri gatewayWithPostdial = gatewayUri;
if (gatewayUri != null && !TextUtils.isEmpty(postdialDigits)) {
gatewayWithPostdial = new Uri.Builder()
.scheme(gatewayUri.getScheme())
.encodedOpaquePart(gatewayUri.getSchemeSpecificPart() + postdialDigits)
.build();
}
return new GatewayInfo(gatewayPackageName, gatewayWithPostdial, destinationUri);
}
return null;
}
}