概述
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,国际拨号的时候不需要;最后是目的地的号码,座机或者手机号码。
android中的使用
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加号替换
由于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在
vendor/mediatek/proprietary/frameworks/common/src/com/mediatek/common/telephony/cdma/ICdmaCallTrackerExt.java
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文件夹下,加号处理的具体逻辑还是很复杂的,不过对外使用很清晰。