1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.mms.service; 18 19 import android.content.ContentValues; 20 import android.content.Context; 21 import android.content.res.XmlResourceParser; 22 import android.os.Bundle; 23 import android.os.SystemProperties; 24 import android.telephony.TelephonyManager; 25 import android.text.TextUtils; 26 import android.util.Base64; 27 import android.util.Log; 28 29 import java.io.UnsupportedEncodingException; 30 import java.util.Iterator; 31 import java.util.Map; 32 import java.util.concurrent.ConcurrentHashMap; 33 34 /** 35 * This class manages a cached copy of current MMS configuration key values 36 * 37 * The steps to add a key 38 * 1. Add a String constant for the key 39 * 2. Add a default value for the key by putting a typed value to DEFAULTS 40 * (null means String type only) 41 * 3. Add a getter for the key 42 * 4. Add key/value for relevant mms_config.xml of a specific carrier (mcc/mnc) 43 */ 44 public class MmsConfig { 45 private static final String TAG = MmsService.TAG; 46 47 private static final String DEFAULT_HTTP_KEY_X_WAP_PROFILE = "x-wap-profile"; 48 49 private static final int MAX_IMAGE_HEIGHT = 480; 50 private static final int MAX_IMAGE_WIDTH = 640; 51 private static final int MAX_TEXT_LENGTH = 2000; 52 53 /* 54 * MmsConfig keys. These have to stay in sync with the MMS_CONFIG_* values defined in 55 * SmsManager. 56 */ 57 public static final String CONFIG_ENABLED_MMS = "enabledMMS"; 58 // In case of single segment wap push message, this CONFIG_ENABLED_TRANS_ID indicates whether 59 // TransactionID should be appended to URI or not. 60 public static final String CONFIG_ENABLED_TRANS_ID = "enabledTransID"; 61 public static final String CONFIG_ENABLED_NOTIFY_WAP_MMSC = "enabledNotifyWapMMSC"; 62 public static final String CONFIG_ALIAS_ENABLED = "aliasEnabled"; 63 public static final String CONFIG_ALLOW_ATTACH_AUDIO = "allowAttachAudio"; 64 // If CONFIG_ENABLE_MULTIPART_SMS is true, long sms messages are always sent as multi-part sms 65 // messages, with no checked limit on the number of segments. 66 // If CONFIG_ENABLE_MULTIPART_SMS is false, then as soon as the user types a message longer 67 // than a single segment (i.e. 140 chars), then the message will turn into and be sent 68 // as an mms message or separate, independent SMS messages 69 // (which is dependent on CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES flag). 70 // This feature exists for carriers that don't support multi-part sms's. 71 public static final String CONFIG_ENABLE_MULTIPART_SMS = "enableMultipartSMS"; 72 public static final String CONFIG_ENABLE_SMS_DELIVERY_REPORTS = "enableSMSDeliveryReports"; 73 // If CONFIG_ENABLE_GROUP_MMS is true, a message with multiple recipients, 74 // regardless of contents, will be sent as a single MMS message with multiple "TO" fields set 75 // for each recipient. 76 // If CONFIG_ENABLE_GROUP_MMS is false, the group MMS setting/preference will be hidden 77 // in the settings activity. 78 public static final String CONFIG_ENABLE_GROUP_MMS = "enableGroupMms"; 79 // If this is true, then we should read the content_disposition field of an MMS part 80 // Check wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21 81 // Most carriers support it except Sprint. 82 // There is a system resource defining it: 83 // com.android.internal.R.bool.config_mms_content_disposition_support. 84 // But Shem can not read it. Add here so that we can configure for different carriers. 85 public static final String CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION = 86 "supportMmsContentDisposition"; 87 // if true, show the cell broadcast (amber alert) in the SMS settings. Some carriers 88 // don't want this shown. 89 public static final String CONFIG_CELL_BROADCAST_APP_LINKS = "config_cellBroadcastAppLinks"; 90 // If this is true, we will send multipart SMS as separate SMS messages 91 public static final String CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES = 92 "sendMultipartSmsAsSeparateMessages"; 93 // FLAG(ywen): the following two is not supported yet. 94 public static final String CONFIG_ENABLE_MMS_READ_REPORTS = "enableMMSReadReports"; 95 public static final String CONFIG_ENABLE_MMS_DELIVERY_REPORTS = "enableMMSDeliveryReports"; 96 public static final String CONFIG_MAX_MESSAGE_SIZE = "maxMessageSize"; // in bytes 97 public static final String CONFIG_MAX_IMAGE_HEIGHT = "maxImageHeight"; // in pixels 98 public static final String CONFIG_MAX_IMAGE_WIDTH = "maxImageWidth"; // in pixels 99 public static final String CONFIG_RECIPIENT_LIMIT = "recipientLimit"; 100 public static final String CONFIG_HTTP_SOCKET_TIMEOUT = "httpSocketTimeout"; 101 public static final String CONFIG_ALIAS_MIN_CHARS = "aliasMinChars"; 102 public static final String CONFIG_ALIAS_MAX_CHARS = "aliasMaxChars"; 103 // If CONFIG_ENABLE_MULTIPART_SMS is true and CONFIG_SMS_TO_MMS_TEXT_THRESHOLD > 1, 104 // then multi-part SMS messages will be converted into a single mms message. 105 // For example, if the mms_config.xml file specifies <int name="smsToMmsTextThreshold">4</int>, 106 // then on the 5th sms segment, the message will be converted to an mms. 107 public static final String CONFIG_SMS_TO_MMS_TEXT_THRESHOLD = "smsToMmsTextThreshold"; 108 // LGU+ temporarily requires any SMS message longer than 80 bytes to be sent as MMS 109 // see b/12122333 110 public static final String CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD = 111 "smsToMmsTextLengthThreshold"; 112 public static final String CONFIG_MAX_MESSAGE_TEXT_SIZE = "maxMessageTextSize"; 113 // maximum number of characters allowed for mms subject 114 public static final String CONFIG_MAX_SUBJECT_LENGTH = "maxSubjectLength"; 115 public static final String CONFIG_UA_PROF_TAG_NAME = "uaProfTagName"; 116 public static final String CONFIG_USER_AGENT = "userAgent"; 117 public static final String CONFIG_UA_PROF_URL = "uaProfUrl"; 118 public static final String CONFIG_HTTP_PARAMS = "httpParams"; 119 // Email gateway alias support, including the master switch and different rules 120 public static final String CONFIG_EMAIL_GATEWAY_NUMBER = "emailGatewayNumber"; 121 // String to append to the NAI header, e.g. ":pcs" 122 public static final String CONFIG_NAI_SUFFIX = "naiSuffix"; 123 124 /* 125 * Key types 126 */ 127 public static final String KEY_TYPE_INT = "int"; 128 public static final String KEY_TYPE_BOOL = "bool"; 129 public static final String KEY_TYPE_STRING = "string"; 130 131 /* 132 * Macro names 133 */ 134 // The raw phone number from TelephonyManager.getLine1Number 135 public static final String MACRO_LINE1 = "LINE1"; 136 // NAI (Network Access Identifier), used by Sprint for authentication 137 public static final String MACRO_NAI = "NAI"; 138 139 // Default values. This is read-only. Don't write into it. 140 // This provides the info on valid keys, their types and default values 141 private static final Map<String, Object> DEFAULTS = new ConcurrentHashMap<String, Object>(); 142 static { 143 DEFAULTS.put(CONFIG_ENABLED_MMS, Boolean.valueOf(true)); 144 DEFAULTS.put(CONFIG_ENABLED_TRANS_ID, Boolean.valueOf(false)); 145 DEFAULTS.put(CONFIG_ENABLED_NOTIFY_WAP_MMSC, Boolean.valueOf(false)); 146 DEFAULTS.put(CONFIG_ALIAS_ENABLED, Boolean.valueOf(false)); 147 DEFAULTS.put(CONFIG_ALLOW_ATTACH_AUDIO, Boolean.valueOf(true)); 148 DEFAULTS.put(CONFIG_ENABLE_MULTIPART_SMS, Boolean.valueOf(true)); 149 DEFAULTS.put(CONFIG_ENABLE_SMS_DELIVERY_REPORTS, Boolean.valueOf(true)); 150 DEFAULTS.put(CONFIG_ENABLE_GROUP_MMS, Boolean.valueOf(true)); 151 DEFAULTS.put(CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, Boolean.valueOf(true)); 152 DEFAULTS.put(CONFIG_CELL_BROADCAST_APP_LINKS, Boolean.valueOf(true)); 153 DEFAULTS.put(CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES, Boolean.valueOf(false)); 154 DEFAULTS.put(CONFIG_ENABLE_MMS_READ_REPORTS, Boolean.valueOf(false)); 155 DEFAULTS.put(CONFIG_ENABLE_MMS_DELIVERY_REPORTS, Boolean.valueOf(false)); 156 DEFAULTS.put(CONFIG_MAX_MESSAGE_SIZE, Integer.valueOf(300 * 1024)); 157 DEFAULTS.put(CONFIG_MAX_IMAGE_HEIGHT, Integer.valueOf(MAX_IMAGE_HEIGHT)); 158 DEFAULTS.put(CONFIG_MAX_IMAGE_WIDTH, Integer.valueOf(MAX_IMAGE_WIDTH)); 159 DEFAULTS.put(CONFIG_RECIPIENT_LIMIT, Integer.valueOf(Integer.MAX_VALUE)); 160 DEFAULTS.put(CONFIG_HTTP_SOCKET_TIMEOUT, Integer.valueOf(60 * 1000)); 161 DEFAULTS.put(CONFIG_ALIAS_MIN_CHARS, Integer.valueOf(2)); 162 DEFAULTS.put(CONFIG_ALIAS_MAX_CHARS, Integer.valueOf(48)); 163 DEFAULTS.put(CONFIG_SMS_TO_MMS_TEXT_THRESHOLD, Integer.valueOf(-1)); 164 DEFAULTS.put(CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD, Integer.valueOf(-1)); 165 DEFAULTS.put(CONFIG_MAX_MESSAGE_TEXT_SIZE, Integer.valueOf(-1)); 166 DEFAULTS.put(CONFIG_MAX_SUBJECT_LENGTH, Integer.valueOf(40)); 167 DEFAULTS.put(CONFIG_UA_PROF_TAG_NAME, DEFAULT_HTTP_KEY_X_WAP_PROFILE); 168 DEFAULTS.put(CONFIG_USER_AGENT, ""); 169 DEFAULTS.put(CONFIG_UA_PROF_URL, ""); 170 DEFAULTS.put(CONFIG_HTTP_PARAMS, ""); 171 DEFAULTS.put(CONFIG_EMAIL_GATEWAY_NUMBER, ""); 172 DEFAULTS.put(CONFIG_NAI_SUFFIX, ""); 173 } 174 175 private final long mSubId; 176 177 /** 178 * This class manages a cached copy of current MMS configuration key values for a particular 179 * subscription id. (See the {@link android.telephony.SubscriptionManager}). 180 * 181 * @param context Context of the particular subscription to load. The context's mcc/mnc 182 * should be set to that of the subscription id 183 * @param subId Subscription id of the mcc/mnc in the context 184 */ 185 public MmsConfig(Context context, long subId) { 186 mSubId = subId; 187 // Load defaults 188 mKeyValues.clear(); 189 mKeyValues.putAll(DEFAULTS); 190 // Load User-Agent and UA profile URL settings 191 loadDeviceUaSettings(context); 192 Log.v(TAG, "MmsConfig: mUserAgent=" + mUserAgent + ", mUaProfUrl=" + mUaProfUrl); 193 // Load mms_config.xml resource overlays 194 loadFromResources(context); 195 Log.v(TAG, "MmsConfig: all settings -- " + mKeyValues); 196 } 197 198 /** 199 * Return the subscription ID associated with this MmsConfig 200 * 201 * @return subId the subId associated with this MmsConfig 202 */ 203 public long getSubId() { 204 return mSubId; 205 } 206 207 /** 208 * Check a key and its type match the predefined keys and corresponding types 209 * 210 * @param key 211 * @param type Including "int" "bool" and "string" 212 * @return True if key and type both matches and false otherwise 213 */ 214 public static boolean isValidKey(String key, String type) { 215 if (!TextUtils.isEmpty(key) && DEFAULTS.containsKey(key)) { 216 Object defVal = DEFAULTS.get(key); 217 Class<?> valueType = defVal != null ? defVal.getClass() : String.class; 218 if (KEY_TYPE_INT.equals(type)) { 219 return valueType == Integer.class; 220 } else if (KEY_TYPE_BOOL.equals(type)) { 221 return valueType == Boolean.class; 222 } else if (KEY_TYPE_STRING.equals(type)) { 223 return valueType == String.class; 224 } 225 } 226 return false; 227 } 228 229 /** 230 * Check a key and its type match the predefined keys and corresponding types 231 * 232 * @param key The key of the config 233 * @param value The value of the config 234 * @return True if key and type both matches and false otherwise 235 */ 236 public static boolean isValidValue(String key, Object value) { 237 if (!TextUtils.isEmpty(key) && DEFAULTS.containsKey(key)) { 238 Object defVal = DEFAULTS.get(key); 239 Class<?> valueType = defVal != null ? defVal.getClass() : String.class; 240 return value.getClass().equals(valueType); 241 } 242 return false; 243 } 244 245 private String mUserAgent = null; 246 private String mUaProfUrl = null; 247 248 // The current values 249 private final Map<String, Object> mKeyValues = new ConcurrentHashMap<String, Object>(); 250 251 /** 252 * Get a config value by its type 253 * 254 * @param key The key of the config 255 * @param type The type of the config value 256 * @return The expected typed value or null if no match 257 */ 258 public Object getValueAsType(String key, String type) { 259 if (isValidKey(key, type)) { 260 return mKeyValues.get(key); 261 } 262 return null; 263 } 264 265 public Bundle getCarrierConfigValues() { 266 final Bundle bundle = new Bundle(); 267 final Iterator<Map.Entry<String, Object>> iter = mKeyValues.entrySet().iterator(); 268 while(iter.hasNext()) { 269 final Map.Entry<String, Object> entry = iter.next(); 270 final String key = entry.getKey(); 271 final Object val = entry.getValue(); 272 Class<?> valueType = val != null ? val.getClass() : String.class; 273 if (valueType == Integer.class) { 274 bundle.putInt(key, (Integer)val); 275 } else if (valueType == Boolean.class) { 276 bundle.putBoolean(key, (Boolean)val); 277 } else if (valueType == String.class) { 278 bundle.putString(key, (String)val); 279 } 280 } 281 return bundle; 282 } 283 284 private String getNullableStringValue(String key) { 285 final Object value = mKeyValues.get(key); 286 if (value != null) { 287 return (String) value; 288 } 289 return null; 290 } 291 292 private void update(String key, String value, String type) { 293 try { 294 if (KEY_TYPE_INT.equals(type)) { 295 mKeyValues.put(key, Integer.parseInt(value)); 296 } else if (KEY_TYPE_BOOL.equals(type)) { 297 mKeyValues.put(key, Boolean.parseBoolean(value)); 298 } else if (KEY_TYPE_STRING.equals(type)){ 299 mKeyValues.put(key, value); 300 } 301 } catch (NumberFormatException e) { 302 Log.e(TAG, "MmsConfig.update: invalid " + key + "," + value + "," + type); 303 } 304 } 305 306 private void loadDeviceUaSettings(Context context) { 307 // load the MMS User agent and UaProfUrl from TelephonyManager APIs 308 final TelephonyManager telephonyManager = 309 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 310 mUserAgent = telephonyManager.getMmsUserAgent(); 311 mUaProfUrl = telephonyManager.getMmsUAProfUrl(); 312 } 313 314 private void loadFromResources(Context context) { 315 Log.d(TAG, "MmsConfig.loadFromResources"); 316 final XmlResourceParser parser = context.getResources().getXml(R.xml.mms_config); 317 final MmsConfigXmlProcessor processor = MmsConfigXmlProcessor.get(parser); 318 processor.setMmsConfigHandler(new MmsConfigXmlProcessor.MmsConfigHandler() { 319 @Override 320 public void process(String key, String value, String type) { 321 update(key, value, type); 322 } 323 }); 324 try { 325 processor.process(); 326 } finally { 327 parser.close(); 328 } 329 } 330 331 /** 332 * This class returns corresponding MmsConfig values which can be overridden by 333 * externally provided values. 334 */ 335 public static class Overridden { 336 // The base MmsConfig 337 private final MmsConfig mBase; 338 // The overridden values 339 private final Bundle mOverrides; 340 341 public Overridden(MmsConfig base, Bundle overrides) { 342 mBase = base; 343 mOverrides = overrides; 344 } 345 346 private int getInt(String key) { 347 final Integer def = (Integer) mBase.mKeyValues.get(key); 348 return mOverrides != null ? mOverrides.getInt(key, def) : def; 349 } 350 351 private boolean getBoolean(String key) { 352 final Boolean def = (Boolean) mBase.mKeyValues.get(key); 353 return mOverrides != null ? mOverrides.getBoolean(key, def) : def; 354 } 355 356 private String getString(String key) { 357 if (mOverrides != null && mOverrides.containsKey(key)) { 358 return mOverrides.getString(key); 359 } 360 return mBase.getNullableStringValue(key); 361 } 362 363 public int getSmsToMmsTextThreshold() { 364 return getInt(CONFIG_SMS_TO_MMS_TEXT_THRESHOLD); 365 } 366 367 public int getSmsToMmsTextLengthThreshold() { 368 return getInt(CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD); 369 } 370 371 public boolean getMmsEnabled() { 372 return getBoolean(CONFIG_ENABLED_MMS); 373 } 374 375 public int getMaxMessageSize() { 376 return getInt(CONFIG_MAX_MESSAGE_SIZE); 377 } 378 379 public boolean getTransIdEnabled() { 380 return getBoolean(CONFIG_ENABLED_TRANS_ID); 381 } 382 383 public String getUserAgent() { 384 if (mOverrides != null && mOverrides.containsKey(CONFIG_USER_AGENT)) { 385 return mOverrides.getString(CONFIG_USER_AGENT); 386 } 387 return !TextUtils.isEmpty(mBase.mUserAgent) ? 388 mBase.mUserAgent : mBase.getNullableStringValue(CONFIG_USER_AGENT); 389 } 390 391 public String getUaProfTagName() { 392 return getString(CONFIG_UA_PROF_TAG_NAME); 393 } 394 395 public String getUaProfUrl() { 396 if (mOverrides != null && mOverrides.containsKey(CONFIG_UA_PROF_URL)) { 397 return mOverrides.getString(CONFIG_UA_PROF_URL); 398 } 399 return !TextUtils.isEmpty(mBase.mUaProfUrl) ? 400 mBase.mUaProfUrl : mBase.getNullableStringValue(CONFIG_UA_PROF_URL); 401 } 402 403 public String getHttpParams() { 404 return getString(CONFIG_HTTP_PARAMS); 405 } 406 407 public String getEmailGateway() { 408 return getString(CONFIG_EMAIL_GATEWAY_NUMBER); 409 } 410 411 public int getMaxImageHeight() { 412 return getInt(CONFIG_MAX_IMAGE_HEIGHT); 413 } 414 415 public int getMaxImageWidth() { 416 return getInt(CONFIG_MAX_IMAGE_WIDTH); 417 } 418 419 public int getRecipientLimit() { 420 final int limit = getInt(CONFIG_RECIPIENT_LIMIT); 421 return limit < 0 ? Integer.MAX_VALUE : limit; 422 } 423 424 public int getMaxTextLimit() { 425 final int max = getInt(CONFIG_MAX_MESSAGE_TEXT_SIZE); 426 return max > -1 ? max : MAX_TEXT_LENGTH; 427 } 428 429 public int getHttpSocketTimeout() { 430 return getInt(CONFIG_HTTP_SOCKET_TIMEOUT); 431 } 432 433 public boolean getMultipartSmsEnabled() { 434 return getBoolean(CONFIG_ENABLE_MULTIPART_SMS); 435 } 436 437 public boolean getSendMultipartSmsAsSeparateMessages() { 438 return getBoolean(CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES); 439 } 440 441 public boolean getSMSDeliveryReportsEnabled() { 442 return getBoolean(CONFIG_ENABLE_SMS_DELIVERY_REPORTS); 443 } 444 445 public boolean getNotifyWapMMSC() { 446 return getBoolean(CONFIG_ENABLED_NOTIFY_WAP_MMSC); 447 } 448 449 public boolean isAliasEnabled() { 450 return getBoolean(CONFIG_ALIAS_ENABLED); 451 } 452 453 public int getAliasMinChars() { 454 return getInt(CONFIG_ALIAS_MIN_CHARS); 455 } 456 457 public int getAliasMaxChars() { 458 return getInt(CONFIG_ALIAS_MAX_CHARS); 459 } 460 461 public boolean getAllowAttachAudio() { 462 return getBoolean(CONFIG_ALLOW_ATTACH_AUDIO); 463 } 464 465 public int getMaxSubjectLength() { 466 return getInt(CONFIG_MAX_SUBJECT_LENGTH); 467 } 468 469 public boolean getGroupMmsEnabled() { 470 return getBoolean(CONFIG_ENABLE_GROUP_MMS); 471 } 472 473 public boolean getSupportMmsContentDisposition() { 474 return getBoolean(CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION); 475 } 476 477 public boolean getShowCellBroadcast() { 478 return getBoolean(CONFIG_CELL_BROADCAST_APP_LINKS); 479 } 480 481 public String getNaiSuffix() { 482 return getString(CONFIG_NAI_SUFFIX); 483 } 484 485 public boolean isMmsReadReportsEnabled() { 486 return getBoolean(CONFIG_ENABLE_MMS_READ_REPORTS); 487 } 488 489 public boolean isMmsDeliveryReportsEnabled() { 490 return getBoolean(CONFIG_ENABLE_MMS_DELIVERY_REPORTS); 491 } 492 493 /** 494 * Return the HTTP param macro value. 495 * Example: LINE1 returns the phone number, etc. 496 * 497 * @param macro The macro name 498 * @return The value of the defined macro 499 */ 500 public String getHttpParamMacro(Context context, String macro) { 501 if (MACRO_LINE1.equals(macro)) { 502 return getLine1(context); 503 } else if (MACRO_NAI.equals(macro)) { 504 return getNai(); 505 } 506 return null; 507 } 508 509 /** 510 * @return the phone number 511 */ 512 private static String getLine1(Context context) { 513 // TODO: for MSIM, we will need to pass in the subId 514 final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService( 515 Context.TELEPHONY_SERVICE); 516 return telephonyManager.getLine1Number(); 517 } 518 519 /** 520 * @return the NAI (Network Access Identifier) from SystemProperties 521 */ 522 private String getNai() { 523 String nai = SystemProperties.get("persist.radio.cdma.nai"); 524 if (!TextUtils.isEmpty(nai)) { 525 String naiSuffix = getNaiSuffix(); 526 if (!TextUtils.isEmpty(naiSuffix)) { 527 nai = nai + naiSuffix; 528 } 529 byte[] encoded = null; 530 try { 531 encoded = Base64.encode(nai.getBytes("UTF-8"), Base64.NO_WRAP); 532 } catch (UnsupportedEncodingException e) { 533 encoded = Base64.encode(nai.getBytes(), Base64.NO_WRAP); 534 } 535 try { 536 nai = new String(encoded, "UTF-8"); 537 } catch (UnsupportedEncodingException e) { 538 nai = new String(encoded); 539 } 540 } 541 return nai; 542 } 543 } 544 } 545