E.164 是国际电信联盟定义的在PSTN和一些数据网使用的国际公共电话码号方案,同时定义了具体的码号的格式。E.164定义了最大15数字,完整号码有国际呼叫前缀。
E.164号码是MSISDN号码,它是主叫用户为呼叫移动通信网中用户所需拨号的号码。
其格式为:CC+NDC+SN,也可以表示为:国家代码+N1N2N3+H0H1H2H3+ABCD
(CC=国家码,中国为86;NDC=国内目的码;SN=用户号码)
例如给中国广东深圳0755-12345678拨号,处理后的结果是+8675512345678。其中+号表示要进行国际拨号,在拨号到运营商网络时候会自动(gsm会,cdma不一定)转成一个号码(中国就是00,+86在中国打的话其实就是0086),这个号码代表拨号时注册的运营商网络所在国家;86代表目的所在国家代码,中国的是86;755代表国内地区码(中国很大,按地区分,有些小国根本不需要),0755中的0是国内长途接入码,例如国内座机拨打外地手机号要加0,国际拨号的时候不需要;最后是目的地的号码,座机或者手机号码。
e164号码格式在Android framework中很多地方都有出现,格式化的方法见
frameworks/base/telephony/java/android/telephony/PhoneNumberUtils.java
/**
* Formats the specified {@code phoneNumber} to the E.164 representation.
*
* @param phoneNumber the phone number to format.
* @param defaultCountryIso the ISO 3166-1 two letters country code.
* @return the E.164 representation, or null if the given phone number is not valid.
*/
public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) {
return formatNumberInternal(phoneNumber, defaultCountryIso, PhoneNumberFormat.E164);
}
private static String formatNumberInternal(
String rawPhoneNumber, String defaultCountryIso, PhoneNumberFormat formatIdentifier) {
PhoneNumberUtil util = PhoneNumberUtil.getInstance();
try {
PhoneNumber phoneNumber = util.parse(rawPhoneNumber, defaultCountryIso);
if (util.isValidNumber(phoneNumber)) {
if (formatIdentifier == PhoneNumberFormat.RFC3966) {
String postDial = extractPostDialPortion(rawPhoneNumber);
if (postDial != null && postDial.length() > 0) {
phoneNumber = new PhoneNumber().mergeFrom(phoneNumber)
.setExtension(postDial.substring(1));
}
}
return util.format(phoneNumber, formatIdentifier);
}
} catch (NumberParseException ignored) { }
return null;
其中要完成格式化的类参见import:
import com.android.i18n.phonenumbers.NumberParseException;
import com.android.i18n.phonenumbers.PhoneNumberUtil;
import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
import com.android.i18n.phonenumbers.Phonemetadata.PhoneMetadata;
import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
import com.android.i18n.phonenumbers.ShortNumberUtil;
这些都在libphonenumber静态包中,代码目录在external/libphonenumber下
external/libphonenumber/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberUtil.java
public String format(PhoneNumber number, PhoneNumberFormat numberFormat) {
if (number.getNationalNumber() == 0 && number.hasRawInput()) {
// Unparseable numbers that kept their raw input just use that.
// This is the only case where a number can be formatted as E164 without a
// leading '+' symbol (but the original number wasn't parseable anyway).
// TODO: Consider removing the 'if' above so that unparseable
// strings without raw input format to the empty string instead of "+00"
String rawInput = number.getRawInput();
if (rawInput.length() > 0) {
return rawInput;
}
}
StringBuilder formattedNumber = new StringBuilder(20);
format(number, numberFormat, formattedNumber);
return formattedNumber.toString();
}
public void format(PhoneNumber number, PhoneNumberFormat numberFormat,
StringBuilder formattedNumber) {
// Clear the StringBuilder first.
formattedNumber.setLength(0);
int countryCallingCode = number.getCountryCode();
String nationalSignificantNumber = getNationalSignificantNumber(number); //获取国内拨号号码,要处理国内地区码
if (numberFormat == PhoneNumberFormat.E164) {
// Early exit for E164 case (even if the country calling code is invalid) since no formatting
// of the national number needs to be applied. Extensions are not formatted.
formattedNumber.append(nationalSignificantNumber);
prefixNumberWithCountryCallingCode(countryCallingCode, PhoneNumberFormat.E164,
formattedNumber);
return;
}
...
}
private void prefixNumberWithCountryCallingCode(int countryCallingCode,
PhoneNumberFormat numberFormat,
StringBuilder formattedNumber) {
switch (numberFormat) {
case E164:
formattedNumber.insert(0, countryCallingCode).insert(0, PLUS_SIGN); //插入加号和国家码
return;
...
}
}
格式化e164的调用链条如上,最后生成了一个可以用于国际拨号的号码。
由于cdma网络不一定转换加号,所以电信定制机都会加入自动转换加号的功能。mtk贴心的加入了这个功能:
/frameworks/opt/telephony/src/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
/// @}
/// M: CC101: CDMA plus code @{
private ICdmaCallTrackerExt mCdmaCallTrackerExt
= MPlugin.createInstance(ICdmaCallTrackerExt.class.getName());
/// @}
中间加入了mCdmaCallTrackerExt专门处理加号转换,ICdmaCallTrackerExt在
ICdmaCallTrackerExt是接口类,具体实现类在:
vendor/mediatek/proprietary/frameworks/base/packages/FwkPlugin/src/com/mediatek/op/telephony/cdma/CdmaCallTrackerExt.java
public String processPlusCodeForDriverCall(String number, boolean isMt, int typeOfAddress) {
if (isMt && typeOfAddress == PhoneNumberUtils.TOA_International) {
Rlog.d(TAG, "processPlusCodeForDriverCall, before format number:" + number);
if (number != null && number.length() > 0 && number.charAt(0) == '+') {
number = number.substring(1, number.length());
}
number = mPlusCodeUtils.removeIddNddAddPlusCode(number);
Rlog.d(TAG, "processPlusCodeForDriverCall, after format number:" + number);
}
number = PhoneNumberUtils.stringFromStringAndTOA(number, typeOfAddress);
return number;
}
其中可以看出这个类还是委托给PlusCodeProcessor去处理, 代码文件又回到了framework中,mtk加号处理实现的代码都在
frameworks/base/telephony/java/com/mediatek/internal/telephony/cdma/pluscode文件夹下,加号处理的具体逻辑还是很复杂的,不过对外使用很清晰。