Add the core assisted dialing logic to telephony.
This module contains all of the transformation logic,
constraints, settings, tests, and other functionality for the feature.
Test: Unit tests that do not require a ShadowPhoneGlobals or AndroidManifest
Bug: 63995261
Change-Id: Iec93497042e6288965054bba359ac984b8db694e
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index a3b0892..bf465a4 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -50,6 +50,7 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.phone.settings.PhoneAccountSettingsFragment;
import com.android.phone.settings.VoicemailSettingsActivity;
+import com.android.phone.settings.assisteddialing.AssistedDialingSettingActivity;
import com.android.phone.settings.fdn.FdnSetting;
import java.util.List;
@@ -83,10 +84,12 @@
// persistent, they are used as the key to store shared preferences and the name should not be
// changed unless the settings are also migrated.
private static final String VOICEMAIL_SETTING_SCREEN_PREF_KEY = "button_voicemail_category_key";
- private static final String BUTTON_FDN_KEY = "button_fdn_key";
- private static final String BUTTON_RETRY_KEY = "button_auto_retry_key";
+ private static final String BUTTON_FDN_KEY = "button_fdn_key";
+ private static final String BUTTON_RETRY_KEY = "button_auto_retry_key";
private static final String BUTTON_GSM_UMTS_OPTIONS = "button_gsm_more_expand_key";
private static final String BUTTON_CDMA_OPTIONS = "button_cdma_more_expand_key";
+ private static final String BUTTON_ASSISTED_DIALING_KEY =
+ "assisted_dialing_settings_preference_key";
private static final String PHONE_ACCOUNT_SETTINGS_KEY =
"phone_account_settings_preference_screen";
@@ -144,7 +147,7 @@
}
};
builder.setMessage(getResources().getString(
- R.string.enable_video_calling_dialog_msg))
+ R.string.enable_video_calling_dialog_msg))
.setNeutralButton(getResources().getString(
R.string.enable_video_calling_dialog_settings),
networkSettingsClickListener)
@@ -285,11 +288,11 @@
if (mImsMgr.isVtEnabledByPlatform() && mImsMgr.isVtProvisionedOnDevice()
&& (carrierConfig.getBoolean(
- CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS)
+ CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS)
|| mPhone.mDcTracker.isDataEnabled())) {
boolean currentValue =
mImsMgr.isEnhanced4gLteModeSettingEnabledByUser()
- ? PhoneGlobals.getInstance().phoneMgr.isVideoCallingEnabled(
+ ? PhoneGlobals.getInstance().phoneMgr.isVideoCallingEnabled(
getOpPackageName()) : false;
mEnableVideoCalling.setChecked(currentValue);
mEnableVideoCalling.setOnPreferenceChangeListener(this);
@@ -299,7 +302,7 @@
if (mImsMgr.isVolteEnabledByPlatform()
&& !carrierConfig.getBoolean(
- CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
+ CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL)) {
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
/* tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); */
}
@@ -360,6 +363,8 @@
prefSet.removePreference(wifiCallingSettings);
prefSet.removePreference(mEnableVideoCalling);
}
+
+ updateAssistedDialingButton();
}
/**
@@ -407,6 +412,31 @@
mPhone = mSubscriptionInfoHelper.getPhone();
}
+ private void updateAssistedDialingButton() {
+ Log.i(LOG_TAG, "updateAssistedDialingButton, subId: " + mPhone.getSubId());
+ PreferenceScreen preferenceScreen = getPreferenceScreen();
+ Preference preference = preferenceScreen.findPreference(BUTTON_ASSISTED_DIALING_KEY);
+ Intent intent = mSubscriptionInfoHelper.getIntent(AssistedDialingSettingActivity.class);
+ intent.putExtra(SubscriptionInfoHelper.SUB_ID_EXTRA, mPhone.getSubId());
+ preference.setIntent(intent);
+
+ if (!shouldShowAssistedDialingButton()) {
+ preferenceScreen.removePreference(preference);
+ }
+ }
+
+ private boolean shouldShowAssistedDialingButton() {
+ // Allow carriers to disable this feature.
+ PersistableBundle carrierConfig =
+ PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+ if (!carrierConfig.getBoolean(CarrierConfigManager.KEY_ASSISTED_DIALING_ENABLED_BOOL)) {
+ Log.i(LOG_TAG, "shouldShowAssistedDialingButton, disabled by carrier config");
+ return false;
+ }
+
+ return true;
+ }
+
private static void log(String msg) {
Log.d(LOG_TAG, msg);
}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 3fc3c7c..16c6bf7 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -49,6 +49,7 @@
import android.util.Log;
import android.widget.Toast;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.IccCardConstants;
@@ -183,6 +184,8 @@
private final SettingsObserver mSettingsObserver;
+ private final PhoneNumberUtil mPhoneNumberUtil = PhoneNumberUtil.getInstance();
+
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -196,7 +199,7 @@
CarrierConfigManager.KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL)) {
// Some products don't have the concept of a "SIM network lock"
Log.i(LOG_TAG, "Ignoring EVENT_SIM_NETWORK_LOCKED event; "
- + "not showing 'SIM network unlock' PIN entry screen");
+ + "not showing 'SIM network unlock' PIN entry screen");
} else {
// Normal case: show the "SIM network unlock" PIN entry screen.
// The user won't be able to do anything else until
@@ -439,6 +442,10 @@
return mCM;
}
+ public PhoneNumberUtil getPhoneNumberUtil() {
+ return mPhoneNumberUtil;
+ }
+
public PersistableBundle getCarrierConfig() {
return getCarrierConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
}
@@ -652,7 +659,7 @@
private void setRadioPowerOff(Context context) {
Log.i(LOG_TAG, "Turning radio off - airplane");
Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
- PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
+ PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
SystemProperties.set("persist.radio.airplane_mode_on", "1");
Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT, 0);
PhoneUtils.setRadioPower(false);
diff --git a/src/com/android/phone/assisteddialing/AssistedDialingMediator.java b/src/com/android/phone/assisteddialing/AssistedDialingMediator.java
new file mode 100755
index 0000000..f02dae3
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/AssistedDialingMediator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.NonNull;
+import android.telecom.TransformationInfo;
+
+import java.util.Optional;
+
+/**
+ * The core interface for the AssistedDialingMediator.
+ */
+public interface AssistedDialingMediator {
+
+ /**
+ * Returns {@code true} if the current client platform supports Assisted Dialing.
+ */
+ boolean isPlatformEligible();
+
+ /**
+ * Returns the country code in which the library thinks the user typically resides.
+ */
+ Optional<String> userHomeCountryCode();
+
+ /**
+ * Attempts to perform the assisted dialing action on the supplied number.
+ */
+ Optional<TransformationInfo> attemptAssistedDial(@NonNull String numberToTransform);
+}
diff --git a/src/com/android/phone/assisteddialing/AssistedDialingMediatorImpl.java b/src/com/android/phone/assisteddialing/AssistedDialingMediatorImpl.java
new file mode 100755
index 0000000..37ae15d
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/AssistedDialingMediatorImpl.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.NonNull;
+import android.telecom.TransformationInfo;
+import android.util.Log;
+
+import java.util.Optional;
+
+/**
+ * The Mediator for Assisted Dialing.
+ * <p>
+ * <p>This class is responsible for mediating location discovery of the user, determining if the
+ * call is eligible for assisted dialing, and performing the transformation of numbers eligible for
+ * assisted dialing.
+ */
+final class AssistedDialingMediatorImpl implements AssistedDialingMediator {
+
+ private final LocationDetector mLocationDetector;
+ private final NumberTransformer mNumberTransformer;
+ private static final String LOG_TAG = "AssistedDialingMediatorImpl";
+
+ AssistedDialingMediatorImpl(
+ @NonNull LocationDetector locationDetector,
+ @NonNull NumberTransformer numberTransformer) {
+ if (locationDetector == null) {
+ throw new NullPointerException("locationDetector was null");
+ }
+
+ if (numberTransformer == null) {
+ throw new NullPointerException("numberTransformer was null");
+ }
+ this.mLocationDetector = locationDetector;
+ this.mNumberTransformer = numberTransformer;
+ }
+
+ @Override
+ public boolean isPlatformEligible() {
+ // This impl is only instantiated if it passes platform checks in ConcreteCreator,
+ // so we return true here.
+ return true;
+ }
+
+ /**
+ * Returns the country code in which the library thinks the user typically resides.
+ */
+ @Override
+ public Optional<String> userHomeCountryCode() {
+ return mLocationDetector.getUpperCaseUserHomeCountry();
+ }
+
+ /**
+ * Returns an Optional of type String containing the transformed number that was provided. The
+ * transformed number should be capable of dialing out of the User's current country and
+ * successfully connecting with a contact in the User's home country.
+ */
+ @Override
+ public Optional<TransformationInfo> attemptAssistedDial(@NonNull String numberToTransform) {
+ Optional<String> userHomeCountryCode = mLocationDetector.getUpperCaseUserHomeCountry();
+ Optional<String> userRoamingCountryCode = mLocationDetector
+ .getUpperCaseUserRoamingCountry();
+
+ if (!userHomeCountryCode.isPresent() || !userRoamingCountryCode.isPresent()) {
+ Log.i(LOG_TAG,
+ "Unable to determine country codes");
+ return Optional.empty();
+ }
+
+ return mNumberTransformer.doAssistedDialingTransformation(
+ numberToTransform, userHomeCountryCode.get(), userRoamingCountryCode.get());
+ }
+}
diff --git a/src/com/android/phone/assisteddialing/AssistedDialingMediatorStub.java b/src/com/android/phone/assisteddialing/AssistedDialingMediatorStub.java
new file mode 100755
index 0000000..6c282e6
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/AssistedDialingMediatorStub.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.NonNull;
+import android.telecom.TransformationInfo;
+
+import java.util.Optional;
+
+/**
+ * A stub assisted dialing implementation.
+ */
+public final class AssistedDialingMediatorStub implements AssistedDialingMediator {
+
+ /**
+ * Always returns an empty Optional.
+ */
+ @Override
+ public Optional<TransformationInfo> attemptAssistedDial(@NonNull String numberToTransform) {
+ return Optional.empty();
+ }
+
+ /**
+ * Always returns an empty Optional.
+ */
+ @Override
+ public Optional<String> userHomeCountryCode() {
+ return Optional.empty();
+ }
+
+ @Override
+ public boolean isPlatformEligible() {
+ return false;
+ }
+}
diff --git a/src/com/android/phone/assisteddialing/ConcreteCreator.java b/src/com/android/phone/assisteddialing/ConcreteCreator.java
new file mode 100755
index 0000000..a9ea0ab
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/ConcreteCreator.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.content.Context;
+import android.os.UserManager;
+import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+/**
+ * A Creator for AssistedDialingMediators.
+ * <p>
+ * <p>This helps keep the dependencies required by AssistedDialingMediator for assisted dialing
+ * explicit.
+ */
+public final class ConcreteCreator {
+
+ private static final String LOG_TAG = "ConcreteCreator";
+
+ /**
+ * Creates a new AssistedDialingMediator
+ *
+ * @param telephonyManager The telephony manager used to determine user location.
+ * @param context The context used to determine whether or not a provided number is an
+ * emergency
+ * number.
+ * @return An AssistedDialingMediator
+ */
+ public static AssistedDialingMediator createNewAssistedDialingMediator(
+ @NonNull TelephonyManager telephonyManager, @NonNull Context context) {
+
+ if (telephonyManager == null) {
+ Log.i(
+ LOG_TAG,
+ "provided TelephonyManager was null");
+ return new AssistedDialingMediatorStub();
+ }
+ if (context == null) {
+ Log.i(LOG_TAG, "provided context was null");
+ return new AssistedDialingMediatorStub();
+ }
+
+ UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ if (!userManager.isUserUnlocked()) {
+ // To avoid any issues reading preferences, we disable the feature when the user is in a
+ // locked state.
+ Log.i(LOG_TAG, "user is locked");
+ return new AssistedDialingMediatorStub();
+ }
+
+ if (!PreferenceManager.getDefaultSharedPreferences(context)
+ .getBoolean(context.getString(
+ R.string.assisted_dialing_setting_toggle_key), true)) {
+ Log.i(LOG_TAG, "disabled by local setting");
+
+ return new AssistedDialingMediatorStub();
+ }
+
+ Constraints constraints = new Constraints(context, getCountryCodeProvider());
+ return new AssistedDialingMediatorImpl(
+ new LocationDetector(
+ telephonyManager,
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(context.getString(
+ R.string.assisted_dialing_setting_cc_key), null)),
+ new NumberTransformer(constraints));
+ }
+
+ /**
+ * Returns a CountryCodeProvider responsible for providing countries eligible for
+ * assisted Dialing
+ */
+ public static CountryCodeProvider getCountryCodeProvider() {
+ return new CountryCodeProvider();
+ }
+}
diff --git a/src/com/android/phone/assisteddialing/Constraints.java b/src/com/android/phone/assisteddialing/Constraints.java
new file mode 100755
index 0000000..f5f0933
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/Constraints.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.i18n.phonenumbers.NumberParseException;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber.CountryCodeSource;
+import com.android.phone.PhoneGlobals;
+
+import java.util.Locale;
+import java.util.Optional;
+
+/**
+ * Ensures that a number is eligible for Assisted Dialing
+ */
+final class Constraints {
+ private final PhoneNumberUtil mPhoneNumberUtil = PhoneGlobals.getInstance()
+ .getPhoneNumberUtil();
+ private final Context mContext;
+ private final CountryCodeProvider mCountryCodeProvider;
+ private static final String LOG_TAG = "Constraints";
+
+ /**
+ * Create a new instance of Constraints.
+ *
+ * @param mContext The mContext used to determine whether or not a number
+ * is an emergency number.
+ * @param configProviderCountryCodes A csv of supported country codes, e.g. "US,CA"
+ */
+ Constraints(@NonNull Context context, @NonNull CountryCodeProvider countryCodeProvider) {
+ this.mContext = context;
+ this.mCountryCodeProvider = countryCodeProvider;
+ }
+
+ /**
+ * Determines whether or not we think Assisted Dialing is possible.
+ *
+ * @param numberToCheck A string containing the phone number.
+ * @param userHomeCountryCode A string containing an ISO 3166-1 alpha-2 country code
+ * representing the user's home country.
+ * @param userRoamingCountryCode A string containing an ISO 3166-1 alpha-2 country code
+ * representing the user's roaming country.
+ * @return A boolean indicating whether or not the provided values are eligible for assisted
+ * dialing.
+ */
+ public boolean meetsPreconditions(
+ @NonNull String numberToCheck,
+ @NonNull String userHomeCountryCode,
+ @NonNull String userRoamingCountryCode) {
+ if (this.mContext == null) {
+ Log.i(LOG_TAG, "Provided context cannot be null");
+ return false;
+ }
+
+ if (this.mCountryCodeProvider == null) {
+ Log.i(LOG_TAG, "Provided configProviderCountryCodes cannot be null");
+ return false;
+ }
+
+ if (TextUtils.isEmpty(numberToCheck)) {
+ Log.i(LOG_TAG, "numberToCheck was empty");
+ return false;
+ }
+
+ if (TextUtils.isEmpty(userHomeCountryCode)) {
+ Log.i(LOG_TAG, "userHomeCountryCode was empty");
+ return false;
+ }
+
+ if (TextUtils.isEmpty(userRoamingCountryCode)) {
+ Log.i(LOG_TAG, "userRoamingCountryCode was empty");
+ return false;
+ }
+
+ userHomeCountryCode = userHomeCountryCode.toUpperCase(Locale.US);
+ userRoamingCountryCode = userRoamingCountryCode.toUpperCase(Locale.US);
+
+ Optional<PhoneNumber> parsedPhoneNumber = parsePhoneNumber(numberToCheck,
+ userHomeCountryCode);
+
+ if (!parsedPhoneNumber.isPresent()) {
+ Log.i(LOG_TAG, "parsedPhoneNumber was empty");
+ return false;
+ }
+
+ return areSupportedCountryCodes(userHomeCountryCode, userRoamingCountryCode)
+ && isUserRoaming(userHomeCountryCode, userRoamingCountryCode)
+ && !isInternationalNumber(parsedPhoneNumber)
+ && !isEmergencyNumber(numberToCheck, mContext)
+ && isValidNumber(parsedPhoneNumber)
+ && !hasExtension(parsedPhoneNumber);
+ }
+
+ /**
+ * Returns a boolean indicating the value equivalence of the provided country codes.
+ */
+ private boolean isUserRoaming(
+ @NonNull String userHomeCountryCode, @NonNull String userRoamingCountryCode) {
+ boolean result = !userHomeCountryCode.equals(userRoamingCountryCode);
+ Log.i(LOG_TAG, "isUserRoaming = " + String.valueOf(result));
+ return result;
+ }
+
+ /**
+ * Returns a boolean indicating the support of both provided country codes for assisted dialing.
+ * Both country codes must be allowed for the return value to be true.
+ */
+ private boolean areSupportedCountryCodes(
+ @NonNull String userHomeCountryCode, @NonNull String userRoamingCountryCode) {
+ if (TextUtils.isEmpty(userHomeCountryCode)) {
+ Log.i(LOG_TAG, "userHomeCountryCode was empty");
+ return false;
+ }
+
+ if (TextUtils.isEmpty(userRoamingCountryCode)) {
+ Log.i(LOG_TAG, "userRoamingCountryCode was empty");
+ return false;
+ }
+
+ boolean result =
+ mCountryCodeProvider.isSupportedCountryCode(userHomeCountryCode)
+ && mCountryCodeProvider.isSupportedCountryCode(userRoamingCountryCode);
+ Log.i("Constraints.areSupportedCountryCodes", String.valueOf(result));
+ return result;
+ }
+
+ /**
+ * A convenience method to take a number as a String and a specified country code, and return a
+ * PhoneNumber object.
+ */
+ private Optional<PhoneNumber> parsePhoneNumber(
+ @NonNull String numberToParse, @NonNull String userHomeCountryCode) {
+ try {
+ return Optional.of(mPhoneNumberUtil.parseAndKeepRawInput(numberToParse,
+ userHomeCountryCode));
+ } catch (NumberParseException e) {
+ Log.i(LOG_TAG, "could not parse the number");
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * Returns a boolean indicating if the provided number is already internationally formatted.
+ */
+ private boolean isInternationalNumber(@NonNull Optional<PhoneNumber> parsedPhoneNumber) {
+
+ if (parsedPhoneNumber.get().hasCountryCode()
+ && parsedPhoneNumber.get().getCountryCodeSource()
+ != CountryCodeSource.FROM_DEFAULT_COUNTRY) {
+ Log.i(
+ LOG_TAG,
+ "phone number already provided the country code");
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a boolean indicating if the provided number has an extension.
+ * <p>
+ * <p>Extensions are currently stripped when formatting a number for mobile dialing, so we don't
+ * want to purposefully truncate a number.
+ */
+ private boolean hasExtension(@NonNull Optional<PhoneNumber> parsedPhoneNumber) {
+
+ if (parsedPhoneNumber.get().hasExtension()
+ && !TextUtils.isEmpty(parsedPhoneNumber.get().getExtension())) {
+ Log.i(LOG_TAG, "phone number has an extension");
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns a boolean indicating if the provided number is considered to be a valid number.
+ */
+ private boolean isValidNumber(@NonNull Optional<PhoneNumber> parsedPhoneNumber) {
+ boolean result = PhoneNumberUtil.getInstance().isValidNumber(parsedPhoneNumber.get());
+ Log.i(LOG_TAG, "isValidNumber = " + String.valueOf(result));
+
+ return result;
+ }
+
+ /**
+ * Returns a boolean indicating if the provided number is an emergency number.
+ */
+ private boolean isEmergencyNumber(@NonNull String numberToCheck, @NonNull Context mContext) {
+ // isEmergencyNumber may depend on network state, so also use isLocalEmergencyNumber when
+ // roaming and out of service.
+ boolean result =
+ PhoneNumberUtils.isEmergencyNumber(numberToCheck)
+ || PhoneNumberUtils.isLocalEmergencyNumber(mContext, numberToCheck);
+ Log.i("Constraints.isEmergencyNumber", String.valueOf(result));
+ return result;
+ }
+}
diff --git a/src/com/android/phone/assisteddialing/CountryCodeProvider.java b/src/com/android/phone/assisteddialing/CountryCodeProvider.java
new file mode 100755
index 0000000..5dac283
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/CountryCodeProvider.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.VisibleForTesting;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A class to provide the appropriate country codes related to assisted dialing.
+ */
+public final class CountryCodeProvider {
+
+ // ISO 3166-1 alpha-2 Country Codes that are eligible for assisted dialing.
+ @VisibleForTesting
+ static final List<String> DEFAULT_COUNTRY_CODES =
+ Arrays.asList(
+ "CA" /* Canada */,
+ "GB" /* United Kingdom */,
+ "JP" /* Japan */,
+ "MX" /* Mexico */,
+ "US" /* United States */);
+
+ /**
+ * Checks whether a supplied country code is supported.
+ */
+ public boolean isSupportedCountryCode(String countryCode) {
+ return DEFAULT_COUNTRY_CODES.contains(countryCode);
+ }
+}
diff --git a/src/com/android/phone/assisteddialing/LocationDetector.java b/src/com/android/phone/assisteddialing/LocationDetector.java
new file mode 100755
index 0000000..a601167
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/LocationDetector.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Locale;
+import java.util.Optional;
+
+/**
+ * LocationDetector is responsible for determining the Roaming location of the User, in addition to
+ * User's home country.
+ */
+final class LocationDetector {
+
+ private final TelephonyManager mTelephonyManager;
+ private final String mUserProvidedHomeCountry;
+ private static final String LOG_TAG = "LocationDetector";
+
+ LocationDetector(
+ @NonNull TelephonyManager telephonyManager, @Nullable String userProvidedHomeCountry) {
+ this.mTelephonyManager = telephonyManager;
+ this.mUserProvidedHomeCountry = userProvidedHomeCountry;
+ }
+
+ /**
+ * Returns what we believe to be the User's home country. This should resolve to
+ * PROPERTY_ICC_OPERATOR_ISO_COUNTRY
+ */
+ public Optional<String> getUpperCaseUserHomeCountry() {
+ if (mTelephonyManager == null) {
+ Log.i(LOG_TAG, "Provided TelephonyManager was null");
+ return Optional.empty();
+ }
+
+
+ if (!TextUtils.isEmpty(mUserProvidedHomeCountry)) {
+ Log.i(
+ LOG_TAG,
+ "user provided home country code");
+ return Optional.of(mUserProvidedHomeCountry.toUpperCase(Locale.US));
+ }
+
+ String simCountryIso = mTelephonyManager.getSimCountryIso();
+ if (simCountryIso != null) {
+ Log.i(LOG_TAG, "using sim country iso");
+ return Optional.of(mTelephonyManager.getSimCountryIso().toUpperCase(Locale.US));
+ }
+ Log.i(LOG_TAG, "user home country was null");
+ return Optional.empty();
+ }
+
+ /**
+ * Returns what we believe to be the User's current (roaming) country
+ */
+ public Optional<String> getUpperCaseUserRoamingCountry() {
+ if (mTelephonyManager == null) {
+ Log.i(LOG_TAG, "Provided TelephonyManager was null");
+ return Optional.empty();
+ }
+
+ String networkCountryIso = mTelephonyManager.getNetworkCountryIso();
+ if (networkCountryIso != null) {
+ return Optional.of(mTelephonyManager.getNetworkCountryIso().toUpperCase(Locale.US));
+ }
+ Log.i(LOG_TAG, "user roaming country was null");
+ return Optional.empty();
+ }
+}
diff --git a/src/com/android/phone/assisteddialing/NumberTransformer.java b/src/com/android/phone/assisteddialing/NumberTransformer.java
new file mode 100755
index 0000000..ea0bd8f
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/NumberTransformer.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 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.phone.assisteddialing;
+
+import android.support.annotation.NonNull;
+import android.telecom.TransformationInfo;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.i18n.phonenumbers.NumberParseException;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
+import com.android.phone.PhoneGlobals;
+
+import java.util.Optional;
+
+/**
+ * Responsible for transforming numbers to make them dialable and valid when roaming.
+ */
+final class NumberTransformer {
+
+ private final PhoneNumberUtil mPhoneNumberUtil = PhoneGlobals.getInstance()
+ .getPhoneNumberUtil();
+ private final Constraints mConstraints;
+ private static final String LOG_TAG = "NumberTransformer";
+
+ NumberTransformer(Constraints constraints) {
+ this.mConstraints = constraints;
+ }
+
+ /**
+ * Returns a boolean for callers to quickly determine whether or not the AssistedDialingMediator
+ * thinks an attempt at assisted dialing is likely to succeed.
+ */
+ public boolean canDoAssistedDialingTransformation(
+ @NonNull String numberToCheck,
+ @NonNull String userHomeCountryCode,
+ @NonNull String userRoamingCountryCode) {
+ return mConstraints.meetsPreconditions(
+ numberToCheck, userHomeCountryCode, userRoamingCountryCode);
+ }
+
+ /**
+ * A method to do assisted dialing transformations.
+ * <p>
+ * <p>The library will do its best to attempt a transformation, but, if for any reason the
+ * transformation fails, we return an empty optional. The operation can be considered a success
+ * when the Optional we return has a value set.
+ */
+ public Optional<TransformationInfo> doAssistedDialingTransformation(
+ String numbertoTransform, String userHomeCountryCode, String userRoamingCountryCode) {
+
+ if (!mConstraints.meetsPreconditions(
+ numbertoTransform, userHomeCountryCode, userRoamingCountryCode)) {
+ Log.i(
+ LOG_TAG,
+ "assisted dialing failed preconditions");
+ return Optional.empty();
+ }
+
+ PhoneNumber phoneNumber;
+ try {
+ phoneNumber = mPhoneNumberUtil.parse(numbertoTransform, userHomeCountryCode);
+ } catch (NumberParseException e) {
+ Log.i(LOG_TAG, "number failed to parse");
+ return Optional.empty();
+ }
+
+ String transformedNumber =
+ mPhoneNumberUtil.formatNumberForMobileDialing(phoneNumber, userRoamingCountryCode,
+ true);
+
+ // formatNumberForMobileDialing may return an empty String.
+ if (TextUtils.isEmpty(transformedNumber)) {
+ Log.i(
+ LOG_TAG,
+ "formatNumberForMobileDialing returned an empty string");
+ return Optional.empty();
+ }
+
+ return Optional.of(
+ new TransformationInfo(
+ numbertoTransform,
+ transformedNumber,
+ userHomeCountryCode,
+ userRoamingCountryCode,
+ phoneNumber.getCountryCode()));
+ }
+}
diff --git a/src/com/android/phone/assisteddialing/res/values/strings.xml b/src/com/android/phone/assisteddialing/res/values/strings.xml
new file mode 100755
index 0000000..f9cb123
--- /dev/null
+++ b/src/com/android/phone/assisteddialing/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<resources>
+ <!-- Key for the assisted dialing setting toggle-->
+ <string name="assisted_dialing_setting_toggle_key" translatable="false">assisted_dialing_setting_toggle_key</string>
+
+ <!-- Key for the assisted dialing home country setting-->
+ <string name="assisted_dialing_setting_cc_key" translatable="false">assisted_dialing_setting_cc_key</string>
+</resources>
\ No newline at end of file
diff --git a/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingActivity.java b/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingActivity.java
new file mode 100755
index 0000000..7386e45
--- /dev/null
+++ b/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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.phone.settings.assisteddialing;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.view.MenuItem;
+
+/**
+ * The Settings Activity for Assisted Dialing.
+ */
+public class AssistedDialingSettingActivity extends PreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ getFragmentManager()
+ .beginTransaction()
+ .replace(android.R.id.content, new AssistedDialingSettingFragment())
+ .commit();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingFragment.java b/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingFragment.java
new file mode 100755
index 0000000..ec255ca
--- /dev/null
+++ b/src/com/android/phone/settings/assisteddialing/AssistedDialingSettingFragment.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2017 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.phone.settings.assisteddialing;
+
+import android.icu.util.ULocale;
+import android.icu.util.ULocale.Builder;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.SwitchPreference;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.phone.assisteddialing.AssistedDialingMediator;
+import com.android.phone.assisteddialing.ConcreteCreator;
+import com.android.phone.assisteddialing.CountryCodeProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * The setting for Assisted Dialing
+ */
+public class AssistedDialingSettingFragment extends PreferenceFragment {
+
+ private CountryCodeProvider mCountryCodeProvider;
+ private AssistedDialingMediator mAssistedDialingMediator;
+ private static final String LOG_TAG = "AssistedDialingSettingFragment";
+
+ final class DisplayNameAndCountryCodeTuple {
+
+ private final CharSequence mCountryDisplayname;
+ private final CharSequence mCountryCode;
+
+ DisplayNameAndCountryCodeTuple(
+ CharSequence mCountryDisplayname,
+ CharSequence mCountryCode) {
+ if (mCountryDisplayname == null) {
+ throw new NullPointerException("Null mCountryDisplayname");
+ }
+ this.mCountryDisplayname = mCountryDisplayname;
+ if (mCountryCode == null) {
+ throw new NullPointerException("Null mCountryCode");
+ }
+ this.mCountryCode = mCountryCode;
+ }
+
+ CharSequence countryDisplayname() {
+ return mCountryDisplayname;
+ }
+
+ CharSequence countryCode() {
+ return mCountryCode;
+ }
+
+ public String toString() {
+ return "DisplayNameAndCountryCodeTuple{"
+ + "mCountryDisplayname=" + mCountryDisplayname + ", "
+ + "mCountryCode=" + mCountryCode
+ + "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof AssistedDialingSettingFragment.DisplayNameAndCountryCodeTuple) {
+ AssistedDialingSettingFragment.DisplayNameAndCountryCodeTuple that = (
+ AssistedDialingSettingFragment.DisplayNameAndCountryCodeTuple) o;
+ return (this.mCountryDisplayname.equals(that.countryDisplayname()))
+ && (this.mCountryCode.equals(that.countryCode()));
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 1;
+ h *= 1000003;
+ h ^= this.mCountryDisplayname.hashCode();
+ h *= 1000003;
+ h ^= this.mCountryCode.hashCode();
+ return h;
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mAssistedDialingMediator =
+ ConcreteCreator.createNewAssistedDialingMediator(
+ getContext().getSystemService(TelephonyManager.class), getContext());
+
+ mCountryCodeProvider =
+ ConcreteCreator.getCountryCodeProvider();
+
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.assisted_dialing_setting);
+ SwitchPreference switchPref =
+ (SwitchPreference)
+ findPreference(getContext().getString(
+ R.string.assisted_dialing_setting_toggle_key));
+
+ ListPreference countryChooserPref =
+ (ListPreference)
+ findPreference(getContext().getString(
+ R.string.assisted_dialing_setting_cc_key));
+
+ updateCountryChoices(countryChooserPref);
+ updateCountryChooserSummary(countryChooserPref);
+
+ countryChooserPref.setOnPreferenceChangeListener(this::updateListSummary);
+ }
+
+ private void updateCountryChooserSummary(ListPreference countryChooserPref) {
+ String defaultSummaryText = countryChooserPref.getEntries()[0].toString();
+
+ if (countryChooserPref.getEntry().equals(defaultSummaryText)) {
+ Optional<String> userHomeCountryCode = mAssistedDialingMediator.userHomeCountryCode();
+ if (userHomeCountryCode.isPresent()) {
+ CharSequence[] entries = countryChooserPref.getEntries();
+ try {
+ CharSequence regionalDisplayName =
+ entries[countryChooserPref.findIndexOfValue(userHomeCountryCode.get())];
+ countryChooserPref.setSummary(
+ getContext()
+ .getString(
+ R.string.assisted_dialing_setting_cc_default_summary,
+ regionalDisplayName));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // This might happen if there is a mismatch between the automatically
+ // detected home country, and the countries currently eligible to select in
+ // the settings.
+ Log.i(
+ LOG_TAG,
+ "Failed to find human readable mapping for country, using default.");
+ }
+ }
+ } else {
+ countryChooserPref.setSummary(countryChooserPref.getEntry());
+ }
+ }
+
+ /**
+ * Filters the default entries in the country chooser by only showing those countries in which
+ * the feature in enabled.
+ */
+ private void updateCountryChoices(ListPreference countryChooserPref) {
+
+ List<DisplayNameAndCountryCodeTuple> defaultCountryChoices =
+ buildDefaultCountryChooserKeysAndValues(countryChooserPref);
+
+ // Always include the default preference.
+ List<CharSequence> newKeys = new ArrayList<>();
+ List<CharSequence> newValues = new ArrayList<>();
+ newKeys.add(countryChooserPref.getEntries()[0]);
+ newValues.add(countryChooserPref.getEntryValues()[0]);
+
+ for (DisplayNameAndCountryCodeTuple tuple : defaultCountryChoices) {
+ if (mCountryCodeProvider.isSupportedCountryCode(tuple.countryCode().toString())) {
+ newKeys.add(tuple.countryDisplayname());
+ newValues.add(tuple.countryCode());
+ }
+ }
+
+ countryChooserPref.setEntries(newKeys.toArray(new CharSequence[newKeys.size()]));
+ countryChooserPref.setEntryValues(newValues.toArray(new CharSequence[newValues.size()]));
+ }
+
+ private List<DisplayNameAndCountryCodeTuple> buildDefaultCountryChooserKeysAndValues(
+ ListPreference countryChooserPref) {
+ CharSequence[] keys = countryChooserPref.getEntries();
+ CharSequence[] values = countryChooserPref.getEntryValues();
+
+ if (keys.length != values.length) {
+ throw new IllegalStateException(
+ "Unexpected mismatch in country chooser key/value size");
+ }
+
+ List<DisplayNameAndCountryCodeTuple> displayNamesandCountryCodes = new ArrayList<>();
+ // getCountry() is actually getRegion() and conforms to the iso standards of input for the
+ // builder.
+ ULocale userLocale =
+ new ULocale.Builder()
+ .setRegion(getResources().getConfiguration().getLocales().get(0)
+ .getCountry())
+ .setLanguage(getResources().getConfiguration().getLocales().get(0)
+ .getLanguage())
+ .build();
+ for (int i = 0; i < keys.length; i++) {
+ ULocale settingRowDisplayCountry = new Builder().setRegion(values[i].toString())
+ .build();
+ String localizedDisplayCountry = settingRowDisplayCountry.getDisplayCountry(userLocale);
+ String settingDisplayName = localizedDisplayCountry + " " + keys[i];
+ displayNamesandCountryCodes.add(
+ new DisplayNameAndCountryCodeTuple(settingDisplayName, values[i]));
+ }
+
+ return displayNamesandCountryCodes;
+ }
+
+ boolean updateListSummary(Preference pref, Object newValue) {
+ ListPreference listPref = (ListPreference) pref;
+ CharSequence[] entries = listPref.getEntries();
+ listPref.setSummary(entries[listPref.findIndexOfValue(newValue.toString())]);
+ return true;
+ }
+
+}
diff --git a/src/com/android/phone/settings/assisteddialing/res/values/strings.xml b/src/com/android/phone/settings/assisteddialing/res/values/strings.xml
new file mode 100755
index 0000000..417191e
--- /dev/null
+++ b/src/com/android/phone/settings/assisteddialing/res/values/strings.xml
@@ -0,0 +1,531 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 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
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for a setting enabling assisted dialing [CHAR LIMIT=40]-->
+ <string name="assisted_dialing_setting_title">Assisted dialing</string>
+
+ <!-- Label for a setting enabling assisted dialing switch preference-->
+ <string name="assisted_dialing_setting_summary">Predict and add a country code when you call while traveling abroad</string>
+
+ <!-- Indicates the default state for the home country selector-->
+ <string name="assisted_dialing_setting_cc_default_summary">Automatically detected • <xliff:g example="United Kingdom (+44)" id="ad_country_code_info">%1$s</xliff:g></string>
+
+ <!-- Indicates the default failure state for the home country selector-->
+ <string name="assisted_dialing_setting_cc_default_summary_fallback">Automatically detected</string>
+
+ <!-- Category title for the country code picker in assisted dialing [CHAR LIMIT=40]-->
+ <string name="assisted_dialing_setting_cc_category_title">Home country</string>
+
+ <!-- Label for the country code picker in assisted dialing [CHAR LIMIT=40]-->
+ <string name="assisted_dialing_setting_cc_title">Default home country</string>
+
+ <!-- Composed from https://www.iso.org/obp/ui/#search and
+ https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/metadata.js#L34 -->
+ <!-- Excluding ['Antarctica', 'Bouvet Island', 'French Southern Territories (the)', 'Heard Island and McDonald Islands', 'Pitcairn', 'South Georgia and the South Sandwich Islands', 'United States Minor Outlying Islands (the)'] -->
+ <!-- DO NOT TRANSLATE. Country codes used in assisted dialing. [CHAR LIMIT=40] -->
+ <string-array name="assisted_dialing_cc_entries" translatable="false">
+ <item>@string/assisted_dialing_setting_cc_default_summary_fallback</item>
+ <item>(+93)</item>
+ <item>(+358)</item>
+ <item>(+355)</item>
+ <item>(+213)</item>
+ <item>(+1)</item>
+ <item>(+376)</item>
+ <item>(+244)</item>
+ <item>(+1)</item>
+ <item>(+1)</item>
+ <item>(+54)</item>
+ <item>(+374)</item>
+ <item>(+297)</item>
+ <item>(+61)</item>
+ <item>(+43)</item>
+ <item>(+994)</item>
+ <item>(+1)</item>
+ <item>(+973)</item>
+ <item>(+880)</item>
+ <item>(+1)</item>
+ <item>(+375)</item>
+ <item>(+32)</item>
+ <item>(+501)</item>
+ <item>(+229)</item>
+ <item>(+1)</item>
+ <item>(+975)</item>
+ <item>(+591)</item>
+ <item>(+599)</item>
+ <item>(+387)</item>
+ <item>(+267)</item>
+ <item>(+55)</item>
+ <item>(+246)</item>
+ <item>(+673)</item>
+ <item>(+359)</item>
+ <item>(+226)</item>
+ <item>(+257)</item>
+ <item>(+238)</item>
+ <item>(+855)</item>
+ <item>(+237)</item>
+ <item>(+1)</item>
+ <item>(+1)</item>
+ <item>(+236)</item>
+ <item>(+235)</item>
+ <item>(+56)</item>
+ <item>(+86)</item>
+ <item>(+61)</item>
+ <item>(+61)</item>
+ <item>(+57)</item>
+ <item>(+269)</item>
+ <item>(+243)</item>
+ <item>(+242)</item>
+ <item>(+682)</item>
+ <item>(+506)</item>
+ <item>(+225)</item>
+ <item>(+385)</item>
+ <item>(+53)</item>
+ <item>(+599)</item>
+ <item>(+357)</item>
+ <item>(+420)</item>
+ <item>(+45)</item>
+ <item>(+253)</item>
+ <item>(+1)</item>
+ <item>(+1)</item>
+ <item>(+593)</item>
+ <item>(+20)</item>
+ <item>(+503)</item>
+ <item>(+240)</item>
+ <item>(+291)</item>
+ <item>(+372)</item>
+ <item>(+251)</item>
+ <item>(+500)</item>
+ <item>(+298)</item>
+ <item>(+679)</item>
+ <item>(+358)</item>
+ <item>(+33)</item>
+ <item>(+594)</item>
+ <item>(+689)</item>
+ <item>(+241)</item>
+ <item>(+220)</item>
+ <item>(+995)</item>
+ <item>(+49)</item>
+ <item>(+233)</item>
+ <item>(+350)</item>
+ <item>(+30)</item>
+ <item>(+299)</item>
+ <item>(+1)</item>
+ <item>(+590)</item>
+ <item>(+1)</item>
+ <item>(+502)</item>
+ <item>(+44)</item>
+ <item>(+224)</item>
+ <item>(+245)</item>
+ <item>(+592)</item>
+ <item>(+509)</item>
+ <item>(+39)</item>
+ <item>(+504)</item>
+ <item>(+852)</item>
+ <item>(+36)</item>
+ <item>(+354)</item>
+ <item>(+91)</item>
+ <item>(+62)</item>
+ <item>(+98)</item>
+ <item>(+964)</item>
+ <item>(+353)</item>
+ <item>(+44)</item>
+ <item>(+972)</item>
+ <item>(+39)</item>
+ <item>(+1)</item>
+ <item>(+81)</item>
+ <item>(+44)</item>
+ <item>(+962)</item>
+ <item>(+7)</item>
+ <item>(+254)</item>
+ <item>(+686)</item>
+ <item>(+850)</item>
+ <item>(+82)</item>
+ <item>(+965)</item>
+ <item>(+996)</item>
+ <item>(+856)</item>
+ <item>(+371)</item>
+ <item>(+961)</item>
+ <item>(+266)</item>
+ <item>(+231)</item>
+ <item>(+218)</item>
+ <item>(+423)</item>
+ <item>(+370)</item>
+ <item>(+352)</item>
+ <item>(+853)</item>
+ <item>(+389)</item>
+ <item>(+261)</item>
+ <item>(+265)</item>
+ <item>(+60)</item>
+ <item>(+960)</item>
+ <item>(+223)</item>
+ <item>(+356)</item>
+ <item>(+692)</item>
+ <item>(+596)</item>
+ <item>(+222)</item>
+ <item>(+230)</item>
+ <item>(+262)</item>
+ <item>(+52)</item>
+ <item>(+691)</item>
+ <item>(+373)</item>
+ <item>(+377)</item>
+ <item>(+976)</item>
+ <item>(+382)</item>
+ <item>(+1)</item>
+ <item>(+212)</item>
+ <item>(+258)</item>
+ <item>(+95)</item>
+ <item>(+264)</item>
+ <item>(+674)</item>
+ <item>(+977)</item>
+ <item>(+31)</item>
+ <item>(+687)</item>
+ <item>(+64)</item>
+ <item>(+505)</item>
+ <item>(+227)</item>
+ <item>(+234)</item>
+ <item>(+683)</item>
+ <item>(+672)</item>
+ <item>(+1)</item>
+ <item>(+47)</item>
+ <item>(+968)</item>
+ <item>(+92)</item>
+ <item>(+680)</item>
+ <item>(+970)</item>
+ <item>(+507)</item>
+ <item>(+675)</item>
+ <item>(+595)</item>
+ <item>(+51)</item>
+ <item>(+63)</item>
+ <item>(+48)</item>
+ <item>(+351)</item>
+ <item>(+1)</item>
+ <item>(+974)</item>
+ <item>(+262)</item>
+ <item>(+40)</item>
+ <item>(+7)</item>
+ <item>(+250)</item>
+ <item>(+590)</item>
+ <item>(+290)</item>
+ <item>(+1)</item>
+ <item>(+1)</item>
+ <item>(+590)</item>
+ <item>(+508)</item>
+ <item>(+1)</item>
+ <item>(+685)</item>
+ <item>(+378)</item>
+ <item>(+239)</item>
+ <item>(+966)</item>
+ <item>(+221)</item>
+ <item>(+381)</item>
+ <item>(+248)</item>
+ <item>(+232)</item>
+ <item>(+65)</item>
+ <item>(+1)</item>
+ <item>(+421)</item>
+ <item>(+386)</item>
+ <item>(+677)</item>
+ <item>(+252)</item>
+ <item>(+27)</item>
+ <item>(+211)</item>
+ <item>(+34)</item>
+ <item>(+94)</item>
+ <item>(+249)</item>
+ <item>(+597)</item>
+ <item>(+47)</item>
+ <item>(+268)</item>
+ <item>(+46)</item>
+ <item>(+41)</item>
+ <item>(+963)</item>
+ <item>(+886)</item>
+ <item>(+992)</item>
+ <item>(+255)</item>
+ <item>(+66)</item>
+ <item>(+670)</item>
+ <item>(+228)</item>
+ <item>(+690)</item>
+ <item>(+676)</item>
+ <item>(+1)</item>
+ <item>(+216)</item>
+ <item>(+90)</item>
+ <item>(+993)</item>
+ <item>(+1)</item>
+ <item>(+688)</item>
+ <item>(+256)</item>
+ <item>(+380)</item>
+ <item>(+971)</item>
+ <item>(+44)</item>
+ <item>(+1)</item>
+ <item>(+598)</item>
+ <item>(+998)</item>
+ <item>(+678)</item>
+ <item>(+58)</item>
+ <item>(+84)</item>
+ <item>(+1)</item>
+ <item>(+1)</item>
+ <item>(+681)</item>
+ <item>(+212)</item>
+ <item>(+967)</item>
+ <item>(+260)</item>
+ <item>(+263)</item>
+ </string-array>
+ <string-array name="assisted_dialing_cc_values" translatable="false">
+ <item></item>
+ <item>AF</item>
+ <item>AX</item>
+ <item>AL</item>
+ <item>DZ</item>
+ <item>AS</item>
+ <item>AD</item>
+ <item>AO</item>
+ <item>AI</item>
+ <item>AG</item>
+ <item>AR</item>
+ <item>AM</item>
+ <item>AW</item>
+ <item>AU</item>
+ <item>AT</item>
+ <item>AZ</item>
+ <item>BS</item>
+ <item>BH</item>
+ <item>BD</item>
+ <item>BB</item>
+ <item>BY</item>
+ <item>BE</item>
+ <item>BZ</item>
+ <item>BJ</item>
+ <item>BM</item>
+ <item>BT</item>
+ <item>BO</item>
+ <item>BQ</item>
+ <item>BA</item>
+ <item>BW</item>
+ <item>BR</item>
+ <item>IO</item>
+ <item>BN</item>
+ <item>BG</item>
+ <item>BF</item>
+ <item>BI</item>
+ <item>CV</item>
+ <item>KH</item>
+ <item>CM</item>
+ <item>CA</item>
+ <item>KY</item>
+ <item>CF</item>
+ <item>TD</item>
+ <item>CL</item>
+ <item>CN</item>
+ <item>CX</item>
+ <item>CC</item>
+ <item>CO</item>
+ <item>KM</item>
+ <item>CD</item>
+ <item>CG</item>
+ <item>CK</item>
+ <item>CR</item>
+ <item>CI</item>
+ <item>HR</item>
+ <item>CU</item>
+ <item>CW</item>
+ <item>CY</item>
+ <item>CZ</item>
+ <item>DK</item>
+ <item>DJ</item>
+ <item>DM</item>
+ <item>DO</item>
+ <item>EC</item>
+ <item>EG</item>
+ <item>SV</item>
+ <item>GQ</item>
+ <item>ER</item>
+ <item>EE</item>
+ <item>ET</item>
+ <item>FK</item>
+ <item>FO</item>
+ <item>FJ</item>
+ <item>FI</item>
+ <item>FR</item>
+ <item>GF</item>
+ <item>PF</item>
+ <item>GA</item>
+ <item>GM</item>
+ <item>GE</item>
+ <item>DE</item>
+ <item>GH</item>
+ <item>GI</item>
+ <item>GR</item>
+ <item>GL</item>
+ <item>GD</item>
+ <item>GP</item>
+ <item>GU</item>
+ <item>GT</item>
+ <item>GG</item>
+ <item>GN</item>
+ <item>GW</item>
+ <item>GY</item>
+ <item>HT</item>
+ <item>VA</item>
+ <item>HN</item>
+ <item>HK</item>
+ <item>HU</item>
+ <item>IS</item>
+ <item>IN</item>
+ <item>ID</item>
+ <item>IR</item>
+ <item>IQ</item>
+ <item>IE</item>
+ <item>IM</item>
+ <item>IL</item>
+ <item>IT</item>
+ <item>JM</item>
+ <item>JP</item>
+ <item>JE</item>
+ <item>JO</item>
+ <item>KZ</item>
+ <item>KE</item>
+ <item>KI</item>
+ <item>KP</item>
+ <item>KR</item>
+ <item>KW</item>
+ <item>KG</item>
+ <item>LA</item>
+ <item>LV</item>
+ <item>LB</item>
+ <item>LS</item>
+ <item>LR</item>
+ <item>LY</item>
+ <item>LI</item>
+ <item>LT</item>
+ <item>LU</item>
+ <item>MO</item>
+ <item>MK</item>
+ <item>MG</item>
+ <item>MW</item>
+ <item>MY</item>
+ <item>MV</item>
+ <item>ML</item>
+ <item>MT</item>
+ <item>MH</item>
+ <item>MQ</item>
+ <item>MR</item>
+ <item>MU</item>
+ <item>YT</item>
+ <item>MX</item>
+ <item>FM</item>
+ <item>MD</item>
+ <item>MC</item>
+ <item>MN</item>
+ <item>ME</item>
+ <item>MS</item>
+ <item>MA</item>
+ <item>MZ</item>
+ <item>MM</item>
+ <item>NA</item>
+ <item>NR</item>
+ <item>NP</item>
+ <item>NL</item>
+ <item>NC</item>
+ <item>NZ</item>
+ <item>NI</item>
+ <item>NE</item>
+ <item>NG</item>
+ <item>NU</item>
+ <item>NF</item>
+ <item>MP</item>
+ <item>NO</item>
+ <item>OM</item>
+ <item>PK</item>
+ <item>PW</item>
+ <item>PS</item>
+ <item>PA</item>
+ <item>PG</item>
+ <item>PY</item>
+ <item>PE</item>
+ <item>PH</item>
+ <item>PL</item>
+ <item>PT</item>
+ <item>PR</item>
+ <item>QA</item>
+ <item>RE</item>
+ <item>RO</item>
+ <item>RU</item>
+ <item>RW</item>
+ <item>BL</item>
+ <item>SH</item>
+ <item>KN</item>
+ <item>LC</item>
+ <item>MF</item>
+ <item>PM</item>
+ <item>VC</item>
+ <item>WS</item>
+ <item>SM</item>
+ <item>ST</item>
+ <item>SA</item>
+ <item>SN</item>
+ <item>RS</item>
+ <item>SC</item>
+ <item>SL</item>
+ <item>SG</item>
+ <item>SX</item>
+ <item>SK</item>
+ <item>SI</item>
+ <item>SB</item>
+ <item>SO</item>
+ <item>ZA</item>
+ <item>SS</item>
+ <item>ES</item>
+ <item>LK</item>
+ <item>SD</item>
+ <item>SR</item>
+ <item>SJ</item>
+ <item>SZ</item>
+ <item>SE</item>
+ <item>CH</item>
+ <item>SY</item>
+ <item>TW</item>
+ <item>TJ</item>
+ <item>TZ</item>
+ <item>TH</item>
+ <item>TL</item>
+ <item>TG</item>
+ <item>TK</item>
+ <item>TO</item>
+ <item>TT</item>
+ <item>TN</item>
+ <item>TR</item>
+ <item>TM</item>
+ <item>TC</item>
+ <item>TV</item>
+ <item>UG</item>
+ <item>UA</item>
+ <item>AE</item>
+ <item>GB</item>
+ <item>US</item>
+ <item>UY</item>
+ <item>UZ</item>
+ <item>VU</item>
+ <item>VE</item>
+ <item>VN</item>
+ <item>VG</item>
+ <item>VI</item>
+ <item>WF</item>
+ <item>EH</item>
+ <item>YE</item>
+ <item>ZM</item>
+ <item>ZW</item>
+ </string-array>
+</resources>
diff --git a/src/com/android/phone/settings/assisteddialing/res/xml/assisted_dialing_setting.xml b/src/com/android/phone/settings/assisteddialing/res/xml/assisted_dialing_setting.xml
new file mode 100755
index 0000000..9fb61a1
--- /dev/null
+++ b/src/com/android/phone/settings/assisteddialing/res/xml/assisted_dialing_setting.xml
@@ -0,0 +1,38 @@
+<!--
+ ~ Copyright (C) 2017 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.
+ -->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <SwitchPreference
+ android:defaultValue="true"
+ android:key="@string/assisted_dialing_setting_toggle_key"
+ android:summary="@string/assisted_dialing_setting_summary"
+ android:title="@string/assisted_dialing_setting_title"/>
+
+ <PreferenceCategory
+ android:title="@string/assisted_dialing_setting_cc_category_title">
+ <ListPreference
+ android:dependency="@string/assisted_dialing_setting_toggle_key"
+ android:defaultValue=""
+ android:entries="@array/assisted_dialing_cc_entries"
+ android:entryValues="@array/assisted_dialing_cc_values"
+ android:key="@string/assisted_dialing_setting_cc_key"
+ android:summary="@string/assisted_dialing_setting_cc_default_summary_fallback"
+ android:title="@string/assisted_dialing_setting_cc_title"/>
+ </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 3614148..fe75006 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -31,6 +31,7 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telecom.TransformationInfo;
import android.telecom.VideoProfile;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
@@ -55,6 +56,8 @@
import com.android.phone.MMIDialogActivity;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
+import com.android.phone.assisteddialing.AssistedDialingMediator;
+import com.android.phone.assisteddialing.ConcreteCreator;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -63,6 +66,7 @@
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import java.util.Optional;
import java.util.Queue;
import java.util.regex.Pattern;
@@ -77,39 +81,39 @@
private final TelephonyConnectionServiceProxy mTelephonyConnectionServiceProxy =
new TelephonyConnectionServiceProxy() {
- @Override
- public Collection<Connection> getAllConnections() {
- return TelephonyConnectionService.this.getAllConnections();
- }
- @Override
- public void addConference(TelephonyConference mTelephonyConference) {
- TelephonyConnectionService.this.addConference(mTelephonyConference);
- }
- @Override
- public void addConference(ImsConference mImsConference) {
- TelephonyConnectionService.this.addConference(mImsConference);
- }
- @Override
- public void removeConnection(Connection connection) {
- TelephonyConnectionService.this.removeConnection(connection);
- }
- @Override
- public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
- Connection connection) {
- TelephonyConnectionService.this
- .addExistingConnection(phoneAccountHandle, connection);
- }
- @Override
- public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
- Connection connection, Conference conference) {
- TelephonyConnectionService.this
- .addExistingConnection(phoneAccountHandle, connection, conference);
- }
- @Override
- public void addConnectionToConferenceController(TelephonyConnection connection) {
- TelephonyConnectionService.this.addConnectionToConferenceController(connection);
- }
- };
+ @Override
+ public Collection<Connection> getAllConnections() {
+ return TelephonyConnectionService.this.getAllConnections();
+ }
+ @Override
+ public void addConference(TelephonyConference mTelephonyConference) {
+ TelephonyConnectionService.this.addConference(mTelephonyConference);
+ }
+ @Override
+ public void addConference(ImsConference mImsConference) {
+ TelephonyConnectionService.this.addConference(mImsConference);
+ }
+ @Override
+ public void removeConnection(Connection connection) {
+ TelephonyConnectionService.this.removeConnection(connection);
+ }
+ @Override
+ public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
+ Connection connection) {
+ TelephonyConnectionService.this
+ .addExistingConnection(phoneAccountHandle, connection);
+ }
+ @Override
+ public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
+ Connection connection, Conference conference) {
+ TelephonyConnectionService.this
+ .addExistingConnection(phoneAccountHandle, connection, conference);
+ }
+ @Override
+ public void addConnectionToConferenceController(TelephonyConnection connection) {
+ TelephonyConnectionService.this.addConnectionToConferenceController(connection);
+ }
+ };
private final Connection.Listener mConnectionListener = new Connection.Listener() {
@Override
@@ -243,16 +247,17 @@
*/
private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
new TelephonyConnection.TelephonyConnectionListener() {
- @Override
- public void onOriginalConnectionConfigured(TelephonyConnection c) {
- addConnectionToConferenceController(c);
- }
+ @Override
+ public void onOriginalConnectionConfigured(TelephonyConnection c) {
+ addConnectionToConferenceController(c);
+ }
- @Override
- public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {
- retryOutgoingOriginalConnection(c, isPermanentFailure);
- }
- };
+ @Override
+ public void onOriginalConnectionRetry(TelephonyConnection c,
+ boolean isPermanentFailure) {
+ retryOutgoingOriginalConnection(c, isPermanentFailure);
+ }
+ };
@Override
public void onCreate() {
@@ -399,13 +404,13 @@
// been powered on and isn't in the UNAVAILABLE state, even if it is
// reporting the OUT_OF_SERVICE state.
return (phone.getState() == PhoneConstants.State.OFFHOOK)
- || phone.getServiceStateTracker().isRadioOn();
+ || phone.getServiceStateTracker().isRadioOn();
} else {
// It is not an emergency number, so wait until we are in service and ready
// to make calls. This can happen when we power down the radio on bluetooth
// to save power on watches.
return (phone.getState() == PhoneConstants.State.OFFHOOK)
- || serviceState == ServiceState.STATE_IN_SERVICE;
+ || serviceState == ServiceState.STATE_IN_SERVICE;
}
}
});
@@ -431,9 +436,28 @@
// If there was a failure, the resulting connection will not be a TelephonyConnection,
// so don't place the call!
if (resultConnection instanceof TelephonyConnection) {
- if (request.getExtras() != null && request.getExtras().getBoolean(
- TelecomManager.EXTRA_USE_ASSISTED_DIALING, false)) {
- ((TelephonyConnection) resultConnection).setIsUsingAssistedDialing(true);
+ CarrierConfigManager cfgManager = (CarrierConfigManager)
+ phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+
+ boolean hasAssistedDialingExtra = request.getExtras() != null && request.getExtras()
+ .getBoolean(TelecomManager.EXTRA_USE_ASSISTED_DIALING, false);
+ boolean isConfigManagerEnabled = cfgManager != null
+ && cfgManager.getConfigForSubId(phone.getSubId()).getBoolean(
+ CarrierConfigManager.KEY_ASSISTED_DIALING_ENABLED_BOOL);
+
+ if (hasAssistedDialingExtra && isConfigManagerEnabled) {
+ Optional<TransformationInfo> transformedNumber = doAssistedDialing(request,
+ phone);
+ if (transformedNumber.isPresent()) {
+ resultConnection.setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL,
+ transformedNumber.get().getTransformedNumber(), null),
+ resultConnection.getAddressPresentation());
+ resultConnection.putExtra(TelecomManager
+ .EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO,
+ transformedNumber.get());
+ ((TelephonyConnection) resultConnection).setIsUsingAssistedDialing(true);
+ }
+
}
placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
}
@@ -442,6 +466,37 @@
}
/**
+ * Performs Assisted Dialing and updates the state of relevant call objects.
+ */
+ private Optional<TransformationInfo> doAssistedDialing(ConnectionRequest request, Phone phone) {
+ TelephonyManager telephonyManager = phone.getContext().getSystemService(TelephonyManager
+ .class);
+
+ if (telephonyManager == null) {
+ return Optional.empty();
+ }
+
+ TelephonyManager simSpecificTelephonyManager = telephonyManager
+ .createForPhoneAccountHandle(request.getAccountHandle());
+
+ if (simSpecificTelephonyManager == null) {
+ return Optional.empty();
+ }
+
+ AssistedDialingMediator assistedDialingMediator =
+ ConcreteCreator.createNewAssistedDialingMediator(
+ simSpecificTelephonyManager, phone.getContext());
+ if (!assistedDialingMediator.isPlatformEligible()) {
+ return Optional.empty();
+ }
+ String phoneNumber =
+ request.getAddress().getScheme().equals(PhoneAccount.SCHEME_TEL)
+ ? request.getAddress().getSchemeSpecificPart()
+ : "";
+ return assistedDialingMediator.attemptAssistedDial(phoneNumber);
+ }
+
+ /**
* Whether the cellular radio is power off because the device is on Bluetooth.
*/
private boolean isRadioPowerDownOnBluetooth() {
@@ -458,8 +513,8 @@
* Handle the onComplete callback of RadioOnStateListener.
*/
private void handleOnComplete(boolean isRadioReady, boolean isEmergencyNumber,
- Connection originalConnection, ConnectionRequest request, String numberToDial,
- Uri handle, int originalPhoneType) {
+ Connection originalConnection, ConnectionRequest request,
+ String numberToDial, Uri handle, int originalPhoneType) {
// Make sure the Call has not already been canceled by the user.
if (originalConnection.getState() == Connection.STATE_DISCONNECTED) {
Log.i(this, "Call disconnected before the outgoing call was placed. Skipping call "
@@ -520,7 +575,8 @@
}
private Connection getTelephonyConnection(final ConnectionRequest request, final String number,
- boolean isEmergencyNumber, final Uri handle, Phone phone) {
+ boolean isEmergencyNumber, final Uri handle,
+ Phone phone) {
if (phone == null) {
final Context context = getApplicationContext();
@@ -697,7 +753,7 @@
com.android.internal.telephony.Connection originalConnection =
call.getState() == Call.State.WAITING ?
- call.getLatestConnection() : call.getEarliestConnection();
+ call.getLatestConnection() : call.getEarliestConnection();
if (isOriginalConnectionKnown(originalConnection)) {
Log.i(this, "onCreateIncomingConnection, original connection already registered");
return Connection.createCanceledConnection();
@@ -743,7 +799,7 @@
@Override
public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
- ConnectionRequest request) {
+ ConnectionRequest request) {
Log.i(this, "onCreateUnknownConnection, request: " + request);
// Use the registered emergency Phone if the PhoneAccountHandle is set to Telephony's
// Emergency PhoneAccount
@@ -948,12 +1004,12 @@
// number and then moving it to the back of the queue if it is not a permanent failure cause
// from the modem.
private void updateCachedConnectionPhonePair(TelephonyConnection c,
- boolean isPermanentFailure) {
+ boolean isPermanentFailure) {
// No cache exists, create a new one.
if (mEmergencyRetryCache == null) {
Log.i(this, "updateCachedConnectionPhonePair, cache is null. Generating new cache");
mEmergencyRetryCache = makeCachedConnectionPhonePair(c);
- // Cache is stale, create a new one with the new TelephonyConnection.
+ // Cache is stale, create a new one with the new TelephonyConnection.
} else if (mEmergencyRetryCache.first.get() != c) {
Log.i(this, "updateCachedConnectionPhonePair, cache is stale. Regenerating.");
mEmergencyRetryCache = makeCachedConnectionPhonePair(c);
@@ -1356,10 +1412,10 @@
// this request to add a call.
if (cdmaConf.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
return Connection.createFailedConnection(new DisconnectCause(
- DisconnectCause.RESTRICTED,
- null,
- getResources().getString(R.string.callFailed_cdma_call_limit),
- "merge-capable call exists, prevent flash command."));
+ DisconnectCause.RESTRICTED,
+ null,
+ getResources().getString(R.string.callFailed_cdma_call_limit),
+ "merge-capable call exists, prevent flash command."));
}
}
}