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在 vendor/mediatek/proprietary/frameworks/common/src/com/mediatek/common/telephony/cdma/ICdmaCallTrackerExt.javaICdmaCallTrackerExt是接口类,具体实现类在: 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文件夹下,加号处理的具体逻辑还是很复杂的,不过对外使用很清晰。