content: /*
   * Copyright (C) 2015 Jacob Klinker
   *
   * 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.mms.service_alt;
  
  import android.content.Context;
  import android.content.res.XmlResourceParser;
  import android.os.Build;
  import android.os.Bundle;
  import android.telephony.SubscriptionManager;
  import android.telephony.TelephonyManager;
  import android.text.TextUtils;
  import android.util.Base64;
  import com.klinker.android.logger.Log;
  
  import com.klinker.android.send_message.R;
  
  import java.io.UnsupportedEncodingException;
  import java.lang.reflect.Method;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.concurrent.ConcurrentHashMap;
  
  /**
   * This class manages a cached copy of current MMS configuration key values
   *
   * The steps to add a key
   * 1. Add a String constant for the key
   * 2. Add a default value for the key by putting a typed value to DEFAULTS
   *    (null means String type only)
   * 3. Add a getter for the key
   * 4. Add key/value for relevant mms_config.xml of a specific carrier (mcc/mnc)
   */
  public class MmsConfig {
      private static final String TAG = "MmsConfig";
  
      private static final String DEFAULT_HTTP_KEY_X_WAP_PROFILE = "x-wap-profile";
  
      private static final int MAX_IMAGE_HEIGHT = 480;
      private static final int MAX_IMAGE_WIDTH = 640;
      private static final int MAX_TEXT_LENGTH = 2000;
  
      /*
       * MmsConfig keys. These have to stay in sync with the MMS_CONFIG_* values defined in
       * SmsManager.
       */
      public static final String CONFIG_ENABLED_MMS = "enabledMMS";
      // In case of single segment wap push message, this CONFIG_ENABLED_TRANS_ID indicates whether
      // TransactionID should be appended to URI or not.
      public static final String CONFIG_ENABLED_TRANS_ID = "enabledTransID";
      public static final String CONFIG_ENABLED_NOTIFY_WAP_MMSC = "enabledNotifyWapMMSC";
      public static final String CONFIG_ALIAS_ENABLED = "aliasEnabled";
      public static final String CONFIG_ALLOW_ATTACH_AUDIO = "allowAttachAudio";
      // If CONFIG_ENABLE_MULTIPART_SMS is true, long sms messages are always sent as multi-part sms
      // messages, with no checked limit on the number of segments.
      // If CONFIG_ENABLE_MULTIPART_SMS is false, then as soon as the user types a message longer
      // than a single segment (i.e. 140 chars), then the message will turn into and be sent
      // as an mms message or separate, independent SMS messages
      // (which is dependent on CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES flag).
      // This feature exists for carriers that don't support multi-part sms's.
      public static final String CONFIG_ENABLE_MULTIPART_SMS = "enableMultipartSMS";
      public static final String CONFIG_ENABLE_SMS_DELIVERY_REPORTS = "enableSMSDeliveryReports";
      // If CONFIG_ENABLE_GROUP_MMS is true, a message with multiple recipients,
      // regardless of contents, will be sent as a single MMS message with multiple "TO" fields set
      // for each recipient.
      // If CONFIG_ENABLE_GROUP_MMS is false, the group MMS setting/preference will be hidden
      // in the settings activity.
      public static final String CONFIG_ENABLE_GROUP_MMS = "enableGroupMms";
      // If this is true, then we should read the content_disposition field of an MMS part
      // Check wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
      // Most carriers support it except Sprint.
      // There is a system resource defining it:
      // com.android.internal.R.bool.config_mms_content_disposition_support.
      // But Shem can not read it. Add here so that we can configure for different carriers.
      public static final String CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION =
              "supportMmsContentDisposition";
      // if true, show the cell broadcast (amber alert) in the SMS settings. Some carriers
      // don't want this shown.
      public static final String CONFIG_CELL_BROADCAST_APP_LINKS = "config_cellBroadcastAppLinks";
      // If this is true, we will send multipart SMS as separate SMS messages
      public static final String CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES =
              "sendMultipartSmsAsSeparateMessages";
      // FLAG(ywen): the following two is not supported yet.
      public static final String CONFIG_ENABLE_MMS_READ_REPORTS = "enableMMSReadReports";
      public static final String CONFIG_ENABLE_MMS_DELIVERY_REPORTS = "enableMMSDeliveryReports";
      // Bouygues Telecom (20820) MMSC does not support "charset" with "Content-Type" header
      // It would fail and return 500. See b/18604507
      // If this is false, then we don't add "charset" to "Content-Type"
      public static final String CONFIG_SUPPORT_HTTP_CHARSET_HEADER = "supportHttpCharsetHeader";
      public static final String CONFIG_MAX_MESSAGE_SIZE = "maxMessageSize"; // in bytes
      public static final String CONFIG_MAX_IMAGE_HEIGHT = "maxImageHeight"; // in pixels
      public static final String CONFIG_MAX_IMAGE_WIDTH = "maxImageWidth"; // in pixels
      public static final String CONFIG_RECIPIENT_LIMIT = "recipientLimit";
      public static final String CONFIG_HTTP_SOCKET_TIMEOUT = "httpSocketTimeout";
      public static final String CONFIG_ALIAS_MIN_CHARS = "aliasMinChars";
      public static final String CONFIG_ALIAS_MAX_CHARS = "aliasMaxChars";
      // If CONFIG_ENABLE_MULTIPART_SMS is true and CONFIG_SMS_TO_MMS_TEXT_THRESHOLD > 1,
      // then multi-part SMS messages will be converted into a single mms message.
      // For example, if the mms_config.xml file specifies <int name="smsToMmsTextThreshold">4</int>,
      // then on the 5th sms segment, the message will be converted to an mms.
      public static final String CONFIG_SMS_TO_MMS_TEXT_THRESHOLD = "smsToMmsTextThreshold";
      // LGU+ temporarily requires any SMS message longer than 80 bytes to be sent as MMS
      // see b/12122333
      public static final String CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD =
              "smsToMmsTextLengthThreshold";
      public static final String CONFIG_MAX_MESSAGE_TEXT_SIZE = "maxMessageTextSize";
      // maximum number of characters allowed for mms subject
      public static final String CONFIG_MAX_SUBJECT_LENGTH = "maxSubjectLength";
      public static final String CONFIG_UA_PROF_TAG_NAME = "uaProfTagName";
      public static final String CONFIG_USER_AGENT = "userAgent";
      public static final String CONFIG_UA_PROF_URL = "uaProfUrl";
      public static final String CONFIG_HTTP_PARAMS = "httpParams";
      // Email gateway alias support, including the master switch and different rules
      public static final String CONFIG_EMAIL_GATEWAY_NUMBER = "emailGatewayNumber";
      // String to append to the NAI header, e.g. ":pcs"
      public static final String CONFIG_NAI_SUFFIX = "naiSuffix";
  
      /*
       * Key types
       */
      public static final String KEY_TYPE_INT = "int";
      public static final String KEY_TYPE_BOOL = "bool";
      public static final String KEY_TYPE_STRING = "string";
  
      /*
       * Macro names
       */
      // The raw phone number from TelephonyManager.getLine1Number
      public static final String MACRO_LINE1 = "LINE1";
      // The phone number without country code
      public static final String MACRO_LINE1NOCOUNTRYCODE = "LINE1NOCOUNTRYCODE";
      // NAI (Network Access Identifier), used by Sprint for authentication
      public static final String MACRO_NAI = "NAI";
  
      // Default values. This is read-only. Don't write into it.
      // This provides the info on valid keys, their types and default values
      private static final Map<String, Object> DEFAULTS = new ConcurrentHashMap<String, Object>();
      static {
          DEFAULTS.put(CONFIG_ENABLED_MMS, Boolean.valueOf(true));
          DEFAULTS.put(CONFIG_ENABLED_TRANS_ID, Boolean.valueOf(false));
          DEFAULTS.put(CONFIG_ENABLED_NOTIFY_WAP_MMSC, Boolean.valueOf(false));
          DEFAULTS.put(CONFIG_ALIAS_ENABLED, Boolean.valueOf(false));
          DEFAULTS.put(CONFIG_ALLOW_ATTACH_AUDIO, Boolean.valueOf(true));
          DEFAULTS.put(CONFIG_ENABLE_MULTIPART_SMS, Boolean.valueOf(true));
          DEFAULTS.put(CONFIG_ENABLE_SMS_DELIVERY_REPORTS, Boolean.valueOf(true));
          DEFAULTS.put(CONFIG_ENABLE_GROUP_MMS, Boolean.valueOf(true));
          DEFAULTS.put(CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, Boolean.valueOf(true));
          DEFAULTS.put(CONFIG_CELL_BROADCAST_APP_LINKS, Boolean.valueOf(true));
          DEFAULTS.put(CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES, Boolean.valueOf(false));
          DEFAULTS.put(CONFIG_ENABLE_MMS_READ_REPORTS, Boolean.valueOf(false));
          DEFAULTS.put(CONFIG_ENABLE_MMS_DELIVERY_REPORTS, Boolean.valueOf(false));
          DEFAULTS.put(CONFIG_SUPPORT_HTTP_CHARSET_HEADER, Boolean.valueOf(false));
          DEFAULTS.put(CONFIG_MAX_MESSAGE_SIZE, Integer.valueOf(300 * 1024));
          DEFAULTS.put(CONFIG_MAX_IMAGE_HEIGHT, Integer.valueOf(MAX_IMAGE_HEIGHT));
          DEFAULTS.put(CONFIG_MAX_IMAGE_WIDTH, Integer.valueOf(MAX_IMAGE_WIDTH));
          DEFAULTS.put(CONFIG_RECIPIENT_LIMIT, Integer.valueOf(Integer.MAX_VALUE));
          DEFAULTS.put(CONFIG_HTTP_SOCKET_TIMEOUT, Integer.valueOf(60 * 1000));
          DEFAULTS.put(CONFIG_ALIAS_MIN_CHARS, Integer.valueOf(2));
          DEFAULTS.put(CONFIG_ALIAS_MAX_CHARS, Integer.valueOf(48));
          DEFAULTS.put(CONFIG_SMS_TO_MMS_TEXT_THRESHOLD, Integer.valueOf(-1));
          DEFAULTS.put(CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD, Integer.valueOf(-1));
          DEFAULTS.put(CONFIG_MAX_MESSAGE_TEXT_SIZE, Integer.valueOf(-1));
          DEFAULTS.put(CONFIG_MAX_SUBJECT_LENGTH, Integer.valueOf(40));
          DEFAULTS.put(CONFIG_UA_PROF_TAG_NAME, DEFAULT_HTTP_KEY_X_WAP_PROFILE);
          DEFAULTS.put(CONFIG_USER_AGENT, "");
          DEFAULTS.put(CONFIG_UA_PROF_URL, "");
          DEFAULTS.put(CONFIG_HTTP_PARAMS, "");
          DEFAULTS.put(CONFIG_EMAIL_GATEWAY_NUMBER, "");
          DEFAULTS.put(CONFIG_NAI_SUFFIX, "");
      }
  
      private final int mSubId;
  
      /**
       * This class manages a cached copy of current MMS configuration key values for a particular
       * subscription id. (See the {@link SubscriptionManager}).
       *
       * @param context Context of the particular subscription to load. The context's mcc/mnc
       * should be set to that of the subscription id
       * @param subId Subscription id of the mcc/mnc in the context
       */
      public MmsConfig(Context context, int subId) {
          mSubId = subId;
          // Load defaults
          mKeyValues.clear();
          mKeyValues.putAll(DEFAULTS);
          // Load User-Agent and UA profile URL settings
          loadDeviceUaSettings(context);
          Log.v(TAG, "MmsConfig: mUserAgent=" + mUserAgent + ", mUaProfUrl=" + mUaProfUrl);
          // Load mms_config.xml resource overlays
          loadFromResources(context);
          Log.v(TAG, "MmsConfig: all settings -- " + mKeyValues);
      }
  
      /**
       * This class manages a cached copy of current MMS configuration key values for a particular
       * subscription id. (See the {@link SubscriptionManager}).
       *
       * @param context Context of the particular subscription to load. The context's mcc/mnc
       * should be set to that of the subscription id\
       */
      public MmsConfig(Context context) {
          mSubId = -1;
          // Load defaults
          mKeyValues.clear();
          mKeyValues.putAll(DEFAULTS);
          // Load User-Agent and UA profile URL settings
          loadDeviceUaSettings(context);
          Log.v(TAG, "MmsConfig: mUserAgent=" + mUserAgent + ", mUaProfUrl=" + mUaProfUrl);
          // Load mms_config.xml resource overlays
          loadFromResources(context);
          Log.v(TAG, "MmsConfig: all settings -- " + mKeyValues);
      }
  
      /**
       * Return the subscription ID associated with this MmsConfig
       *
       * @return subId the subId associated with this MmsConfig
       */
      public int getSubId() {
          return mSubId;
      }
  
      /**
       * Check a key and its type match the predefined keys and corresponding types
       *
       * @param key
       * @param type Including "int" "bool" and "string"
       * @return True if key and type both matches and false otherwise
       */
      public static boolean isValidKey(String key, String type) {
          if (!TextUtils.isEmpty(key) && DEFAULTS.containsKey(key)) {
              Object defVal = DEFAULTS.get(key);
              Class<?> valueType = defVal != null ? defVal.getClass() : String.class;
              if (KEY_TYPE_INT.equals(type)) {
                  return valueType == Integer.class;
              } else if (KEY_TYPE_BOOL.equals(type)) {
                  return valueType == Boolean.class;
              } else if (KEY_TYPE_STRING.equals(type)) {
                  return valueType == String.class;
              }
          }
          return false;
      }
  
      /**
       * Check a key and its type match the predefined keys and corresponding types
       *
       * @param key The key of the config
       * @param value The value of the config
       * @return True if key and type both matches and false otherwise
       */
      public static boolean isValidValue(String key, Object value) {
          if (!TextUtils.isEmpty(key) && DEFAULTS.containsKey(key)) {
              Object defVal = DEFAULTS.get(key);
              Class<?> valueType = defVal != null ? defVal.getClass() : String.class;
              return value.getClass().equals(valueType);
          }
          return false;
      }
  
      private String mUserAgent = null;
      private String mUaProfUrl = null;
  
      // The current values
      private final Map<String, Object> mKeyValues = new ConcurrentHashMap<String, Object>();
  
      /**
       * Get a config value by its type
       *
       * @param key The key of the config
       * @param type The type of the config value
       * @return The expected typed value or null if no match
       */
      public Object getValueAsType(String key, String type) {
          if (isValidKey(key, type)) {
              return mKeyValues.get(key);
          }
          return null;
      }
  
      public Bundle getCarrierConfigValues() {
          final Bundle bundle = new Bundle();
          final Iterator<Map.Entry<String, Object>> iter = mKeyValues.entrySet().iterator();
          while(iter.hasNext()) {
              final Map.Entry<String, Object> entry = iter.next();
              final String key = entry.getKey();
              final Object val = entry.getValue();
              Class<?> valueType =  val != null ? val.getClass() : String.class;
              if (valueType == Integer.class) {
                  bundle.putInt(key, (Integer)val);
              } else if (valueType == Boolean.class) {
                  bundle.putBoolean(key, (Boolean)val);
              } else if (valueType == String.class) {
                  bundle.putString(key, (String)val);
              }
          }
          return bundle;
      }
  
      private String getNullableStringValue(String key) {
          final Object value = mKeyValues.get(key);
          if (value != null) {
              return (String) value;
          }
          return null;
      }
  
      private void update(String key, String value, String type) {
          try {
              if (KEY_TYPE_INT.equals(type)) {
                  mKeyValues.put(key, Integer.parseInt(value));
              } else if (KEY_TYPE_BOOL.equals(type)) {
                  mKeyValues.put(key, Boolean.parseBoolean(value));
              } else if (KEY_TYPE_STRING.equals(type)){
                  mKeyValues.put(key, value);
              }
          } catch (NumberFormatException e) {
              Log.e(TAG, "MmsConfig.update: invalid " + key + "," + value + "," + type);
          }
      }
  
      private void loadDeviceUaSettings(Context context) {
          // load the MMS User agent and UaProfUrl from TelephonyManager APIs
          final TelephonyManager telephonyManager =
                  (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
              mUserAgent = telephonyManager.getMmsUserAgent();
              mUaProfUrl = telephonyManager.getMmsUAProfUrl();
              // defaults for nexus 6, seems to work well.
              //mUserAgent = "nexus6";
              //mUaProfUrl = "http://uaprof.motorola.com/phoneconfig/nexus6/Profile/nexus6.rdf";
          } else {
              mUserAgent = "Android Messaging";
              mUaProfUrl = "http://www.gstatic.com/android/hangouts/hangouts_mms_ua_profile.xml";
          }
      }
  
      private void loadFromResources(Context context) {
          Log.d(TAG, "MmsConfig.loadFromResources");
          final XmlResourceParser parser = context.getResources().getXml(R.xml.mms_config);
          final MmsConfigXmlProcessor processor = MmsConfigXmlProcessor.get(parser);
          processor.setMmsConfigHandler(new MmsConfigXmlProcessor.MmsConfigHandler() {
              @Override
              public void process(String key, String value, String type) {
                  update(key, value, type);
              }
          });
          try {
              processor.process();
          } finally {
              parser.close();
          }
      }
  
      /**
       * This class returns corresponding MmsConfig values which can be overridden by
       * externally provided values.
       */
      public static class Overridden {
          // The base MmsConfig
          private final MmsConfig mBase;
          // The overridden values
          private final Bundle mOverrides;
  
          public Overridden(MmsConfig base, Bundle overrides) {
              mBase = base;
              mOverrides = overrides;
          }
  
          private int getInt(String key) {
              final Integer def = (Integer) mBase.mKeyValues.get(key);
              return mOverrides != null ? mOverrides.getInt(key, def) : def;
          }
  
          private boolean getBoolean(String key) {
              final Boolean def = (Boolean) mBase.mKeyValues.get(key);
              return mOverrides != null ? mOverrides.getBoolean(key, def) : def;
          }
  
          private String getString(String key) {
              if (mOverrides != null && mOverrides.containsKey(key)) {
                  return mOverrides.getString(key);
              }
              return mBase.getNullableStringValue(key);
          }
  
          public int getSmsToMmsTextThreshold() {
              return getInt(CONFIG_SMS_TO_MMS_TEXT_THRESHOLD);
          }
  
          public int getSmsToMmsTextLengthThreshold() {
              return getInt(CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD);
          }
  
          public boolean getMmsEnabled() {
              return getBoolean(CONFIG_ENABLED_MMS);
          }
  
          public int getMaxMessageSize() {
              return getInt(CONFIG_MAX_MESSAGE_SIZE);
          }
  
          public boolean getTransIdEnabled() {
              return getBoolean(CONFIG_ENABLED_TRANS_ID);
          }
  
          public String getUserAgent() {
              if (mOverrides != null && mOverrides.containsKey(CONFIG_USER_AGENT)) {
                  return mOverrides.getString(CONFIG_USER_AGENT);
              }
              return !TextUtils.isEmpty(mBase.mUserAgent) ?
                      mBase.mUserAgent : mBase.getNullableStringValue(CONFIG_USER_AGENT);
          }
  
          public String getUaProfTagName() {
              return getString(CONFIG_UA_PROF_TAG_NAME);
          }
  
          public String getUaProfUrl() {
              if (mOverrides != null && mOverrides.containsKey(CONFIG_UA_PROF_URL)) {
                  return mOverrides.getString(CONFIG_UA_PROF_URL);
              }
              return !TextUtils.isEmpty(mBase.mUaProfUrl) ?
                      mBase.mUaProfUrl : mBase.getNullableStringValue(CONFIG_UA_PROF_URL);
          }
  
          public String getHttpParams() {
              return getString(CONFIG_HTTP_PARAMS);
          }
  
          public String getEmailGateway() {
              return getString(CONFIG_EMAIL_GATEWAY_NUMBER);
          }
  
          public int getMaxImageHeight() {
              return getInt(CONFIG_MAX_IMAGE_HEIGHT);
          }
  
          public int getMaxImageWidth() {
              return getInt(CONFIG_MAX_IMAGE_WIDTH);
          }
  
          public int getRecipientLimit() {
              final int limit = getInt(CONFIG_RECIPIENT_LIMIT);
              return limit < 0 ? Integer.MAX_VALUE : limit;
          }
  
          public int getMaxTextLimit() {
              final int max = getInt(CONFIG_MAX_MESSAGE_TEXT_SIZE);
              return max > -1 ? max : MAX_TEXT_LENGTH;
          }
  
          public int getHttpSocketTimeout() {
              return getInt(CONFIG_HTTP_SOCKET_TIMEOUT);
          }
  
          public boolean getMultipartSmsEnabled() {
              return getBoolean(CONFIG_ENABLE_MULTIPART_SMS);
          }
  
          public boolean getSendMultipartSmsAsSeparateMessages() {
              return getBoolean(CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES);
          }
  
          public boolean getSMSDeliveryReportsEnabled() {
              return getBoolean(CONFIG_ENABLE_SMS_DELIVERY_REPORTS);
          }
  
          public boolean getNotifyWapMMSC() {
              return getBoolean(CONFIG_ENABLED_NOTIFY_WAP_MMSC);
          }
  
          public boolean isAliasEnabled() {
              return getBoolean(CONFIG_ALIAS_ENABLED);
          }
  
          public int getAliasMinChars() {
              return getInt(CONFIG_ALIAS_MIN_CHARS);
          }
  
          public int getAliasMaxChars() {
              return getInt(CONFIG_ALIAS_MAX_CHARS);
          }
  
          public boolean getAllowAttachAudio() {
              return getBoolean(CONFIG_ALLOW_ATTACH_AUDIO);
          }
  
          public int getMaxSubjectLength() {
              return getInt(CONFIG_MAX_SUBJECT_LENGTH);
          }
  
          public boolean getGroupMmsEnabled() {
              return getBoolean(CONFIG_ENABLE_GROUP_MMS);
          }
  
          public boolean getSupportMmsContentDisposition() {
              return getBoolean(CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION);
          }
  
          public boolean getShowCellBroadcast() {
              return getBoolean(CONFIG_CELL_BROADCAST_APP_LINKS);
          }
  
          public String getNaiSuffix() {
              return getString(CONFIG_NAI_SUFFIX);
          }
  
          public boolean isMmsReadReportsEnabled() {
              return getBoolean(CONFIG_ENABLE_MMS_READ_REPORTS);
          }
  
          public boolean isMmsDeliveryReportsEnabled() {
              return getBoolean(CONFIG_ENABLE_MMS_DELIVERY_REPORTS);
          }
  
          public boolean getSupportHttpCharsetHeader() {
              return getBoolean(CONFIG_SUPPORT_HTTP_CHARSET_HEADER);
          }
  
          /**
           * Return the HTTP param macro value.
           * Example: LINE1 returns the phone number, etc.
           *
           * @param macro The macro name
           * @return The value of the defined macro
           */
          public String getHttpParamMacro(Context context, String macro) {
              if (MACRO_LINE1.equals(macro)) {
                  return getLine1(context, mBase.getSubId());
              } else if (MACRO_LINE1NOCOUNTRYCODE.equals(macro)) {
                  return getLine1NoCountryCode(context, mBase.getSubId());
              } else if (MACRO_NAI.equals(macro)) {
                  return getNai(context, mBase.getSubId());
              }
              return null;
          }
  
          /**
           * @return the phone number
           */
          private static String getLine1(Context context, int subId) {
              final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
                      Context.TELEPHONY_SERVICE);
  
              if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
                  return telephonyManager.getLine1Number();
              } else {
                  try {
                      Method method = telephonyManager.getClass().getMethod("getLine1NumberForSubscriber", int.class);
                      return (String) method.invoke(telephonyManager, subId);
                  } catch (Exception e) {
                      return telephonyManager.getLine1Number();
                  }
              }
          }
  
          private static String getLine1NoCountryCode(Context context, int subId) {
              final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
                      Context.TELEPHONY_SERVICE);
              return PhoneUtils.getNationalNumber(
                      telephonyManager,
                      subId,
                      getLine1(context, subId));
          }
  
          /**
           * @return the NAI (Network Access Identifier) from SystemProperties
           */
          private String getNai(Context context, int subId) {
              final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
                      Context.TELEPHONY_SERVICE);
  
              String nai = "";
  
              if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
                  nai = SystemPropertiesProxy.get(context, "persist.radio.cdma.nai");
              } else {
                  try {
                      Method method = telephonyManager.getClass().getMethod("getNai", int.class);
                      Method getSlotId = SubscriptionManager.class.getMethod("getSlotId", int.class);
                      nai = (String) method.invoke(telephonyManager, getSlotId.invoke(null, subId));
                  } catch (Exception e) {
                      nai = SystemPropertiesProxy.get(context, "persist.radio.cdma.nai");
                  }
              }
  
              if (Log.isLoggable(TAG, Log.VERBOSE)) {
                  Log.v(TAG, "MmsConfig.getNai: nai=" + nai);
              }
  
              if (!TextUtils.isEmpty(nai)) {
                  String naiSuffix = getNaiSuffix();
                  if (!TextUtils.isEmpty(naiSuffix)) {
                      nai = nai + naiSuffix;
                  }
                  byte[] encoded = null;
                  try {
                      encoded = Base64.encode(nai.getBytes("UTF-8"), Base64.NO_WRAP);
                  } catch (UnsupportedEncodingException e) {
                      encoded = Base64.encode(nai.getBytes(), Base64.NO_WRAP);
                  }
                  try {
                      nai = new String(encoded, "UTF-8");
                  } catch (UnsupportedEncodingException e) {
                      nai = new String(encoded);
                  }
              }
              return nai;
          }
      }
  }
  