概述

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文件夹下,加号处理的具体逻辑还是很复杂的,不过对外使用很清晰。