SmsManager.java revision bc1a6e60c8c0555d47bcf8424af63665c8db062e
1/* 2 * Copyright (C) 2008 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 17package android.telephony; 18 19import android.annotation.SystemApi; 20import android.app.ActivityThread; 21import android.app.PendingIntent; 22import android.content.ActivityNotFoundException; 23import android.content.ContentValues; 24import android.content.Context; 25import android.content.Intent; 26import android.net.Uri; 27import android.os.BaseBundle; 28import android.os.Bundle; 29import android.os.RemoteException; 30import android.os.ServiceManager; 31import android.text.TextUtils; 32import android.util.ArrayMap; 33import android.util.Log; 34 35import com.android.internal.telephony.IMms; 36import com.android.internal.telephony.ISms; 37import com.android.internal.telephony.SmsRawData; 38 39import java.util.ArrayList; 40import java.util.Arrays; 41import java.util.List; 42import java.util.Map; 43 44/* 45 * TODO(code review): Curious question... Why are a lot of these 46 * methods not declared as static, since they do not seem to require 47 * any local object state? Presumably this cannot be changed without 48 * interfering with the API... 49 */ 50 51/** 52 * Manages SMS operations such as sending data, text, and pdu SMS messages. 53 * Get this object by calling the static method {@link #getDefault()}. 54 * 55 * <p>For information about how to behave as the default SMS app on Android 4.4 (API level 19) 56 * and higher, see {@link android.provider.Telephony}. 57 */ 58public final class SmsManager { 59 private static final String TAG = "SmsManager"; 60 /** 61 * A psuedo-subId that represents the default subId at any given time. The actual subId it 62 * represents changes as the default subId is changed. 63 */ 64 private static final int DEFAULT_SUBSCRIPTION_ID = -1002; 65 66 /** Singleton object constructed during class initialization. */ 67 private static final SmsManager sInstance = new SmsManager(DEFAULT_SUBSCRIPTION_ID); 68 private static final Object sLockObject = new Object(); 69 70 /** @hide */ 71 public static final int CELL_BROADCAST_RAN_TYPE_GSM = 0; 72 /** @hide */ 73 public static final int CELL_BROADCAST_RAN_TYPE_CDMA = 1; 74 75 /** SMS record length from TS 51.011 10.5.3 76 * @hide 77 */ 78 public static final int SMS_RECORD_LENGTH = 176; 79 80 /** SMS record length from C.S0023 3.4.27 81 * @hide 82 */ 83 public static final int CDMA_SMS_RECORD_LENGTH = 255; 84 85 private static final Map<Integer, SmsManager> sSubInstances = 86 new ArrayMap<Integer, SmsManager>(); 87 88 /** A concrete subscription id, or the pseudo DEFAULT_SUBSCRIPTION_ID */ 89 private int mSubId; 90 91 /* 92 * Key for the various carrier-dependent configuration values. 93 * Some of the values are used by the system in processing SMS or MMS messages. Others 94 * are provided for the convenience of SMS applications. 95 */ 96 97 /** 98 * Whether to append transaction id to MMS WAP Push M-Notification.ind's content location URI 99 * when constructing the download URL of a new MMS (boolean type) 100 */ 101 public static final String MMS_CONFIG_APPEND_TRANSACTION_ID = 102 CarrierConfigManager.KEY_MMS_APPEND_TRANSACTION_ID_BOOL; 103 /** 104 * Whether MMS is enabled for the current carrier (boolean type) 105 */ 106 public static final String 107 MMS_CONFIG_MMS_ENABLED = CarrierConfigManager.KEY_MMS_MMS_ENABLED_BOOL; 108 /** 109 * Whether group MMS is enabled for the current carrier (boolean type) 110 */ 111 public static final String 112 MMS_CONFIG_GROUP_MMS_ENABLED = CarrierConfigManager.KEY_MMS_GROUP_MMS_ENABLED_BOOL; 113 /** 114 * If this is enabled, M-NotifyResp.ind should be sent to the WAP Push content location instead 115 * of the default MMSC (boolean type) 116 */ 117 public static final String MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED = 118 CarrierConfigManager.KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL; 119 /** 120 * Whether alias is enabled (boolean type) 121 */ 122 public static final String 123 MMS_CONFIG_ALIAS_ENABLED = CarrierConfigManager.KEY_MMS_ALIAS_ENABLED_BOOL; 124 /** 125 * Whether audio is allowed to be attached for MMS messages (boolean type) 126 */ 127 public static final String 128 MMS_CONFIG_ALLOW_ATTACH_AUDIO = CarrierConfigManager.KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL; 129 /** 130 * Whether multipart SMS is enabled (boolean type) 131 */ 132 public static final String MMS_CONFIG_MULTIPART_SMS_ENABLED = 133 CarrierConfigManager.KEY_MMS_MULTIPART_SMS_ENABLED_BOOL; 134 /** 135 * Whether SMS delivery report is enabled (boolean type) 136 */ 137 public static final String MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED = 138 CarrierConfigManager.KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL; 139 /** 140 * Whether content-disposition field should be expected in an MMS PDU (boolean type) 141 */ 142 public static final String MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION = 143 CarrierConfigManager.KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL; 144 /** 145 * Whether multipart SMS should be sent as separate messages 146 */ 147 public static final String MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES = 148 CarrierConfigManager.KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL; 149 /** 150 * Whether MMS read report is enabled (boolean type) 151 */ 152 public static final String MMS_CONFIG_MMS_READ_REPORT_ENABLED = 153 CarrierConfigManager.KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL; 154 /** 155 * Whether MMS delivery report is enabled (boolean type) 156 */ 157 public static final String MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED = 158 CarrierConfigManager.KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL; 159 /** 160 * Max MMS message size in bytes (int type) 161 */ 162 public static final String 163 MMS_CONFIG_MAX_MESSAGE_SIZE = CarrierConfigManager.KEY_MMS_MAX_MESSAGE_SIZE_INT; 164 /** 165 * Max MMS image width (int type) 166 */ 167 public static final String 168 MMS_CONFIG_MAX_IMAGE_WIDTH = CarrierConfigManager.KEY_MMS_MAX_IMAGE_WIDTH_INT; 169 /** 170 * Max MMS image height (int type) 171 */ 172 public static final String 173 MMS_CONFIG_MAX_IMAGE_HEIGHT = CarrierConfigManager.KEY_MMS_MAX_IMAGE_HEIGHT_INT; 174 /** 175 * Limit of recipients of MMS messages (int type) 176 */ 177 public static final String 178 MMS_CONFIG_RECIPIENT_LIMIT = CarrierConfigManager.KEY_MMS_RECIPIENT_LIMIT_INT; 179 /** 180 * Min alias character count (int type) 181 */ 182 public static final String 183 MMS_CONFIG_ALIAS_MIN_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MIN_CHARS_INT; 184 /** 185 * Max alias character count (int type) 186 */ 187 public static final String 188 MMS_CONFIG_ALIAS_MAX_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MAX_CHARS_INT; 189 /** 190 * When the number of parts of a multipart SMS reaches this threshold, it should be converted 191 * into an MMS (int type) 192 */ 193 public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD = 194 CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT; 195 /** 196 * Some carriers require SMS to be converted into MMS when text length reaches this threshold 197 * (int type) 198 */ 199 public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD = 200 CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT; 201 /** 202 * Max message text size (int type) 203 */ 204 public static final String MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE = 205 CarrierConfigManager.KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT; 206 /** 207 * Max message subject length (int type) 208 */ 209 public static final String 210 MMS_CONFIG_SUBJECT_MAX_LENGTH = CarrierConfigManager.KEY_MMS_SUBJECT_MAX_LENGTH_INT; 211 /** 212 * MMS HTTP socket timeout in milliseconds (int type) 213 */ 214 public static final String 215 MMS_CONFIG_HTTP_SOCKET_TIMEOUT = CarrierConfigManager.KEY_MMS_HTTP_SOCKET_TIMEOUT_INT; 216 /** 217 * The name of the UA Prof URL HTTP header for MMS HTTP request (String type) 218 */ 219 public static final String 220 MMS_CONFIG_UA_PROF_TAG_NAME = CarrierConfigManager.KEY_MMS_UA_PROF_TAG_NAME_STRING; 221 /** 222 * The User-Agent header value for MMS HTTP request (String type) 223 */ 224 public static final String 225 MMS_CONFIG_USER_AGENT = CarrierConfigManager.KEY_MMS_USER_AGENT_STRING; 226 /** 227 * The UA Profile URL header value for MMS HTTP request (String type) 228 */ 229 public static final String 230 MMS_CONFIG_UA_PROF_URL = CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING; 231 /** 232 * A list of HTTP headers to add to MMS HTTP request, separated by "|" (String type) 233 */ 234 public static final String 235 MMS_CONFIG_HTTP_PARAMS = CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING; 236 /** 237 * Email gateway number (String type) 238 */ 239 public static final String MMS_CONFIG_EMAIL_GATEWAY_NUMBER = 240 CarrierConfigManager.KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING; 241 /** 242 * The suffix to append to the NAI header value for MMS HTTP request (String type) 243 */ 244 public static final String 245 MMS_CONFIG_NAI_SUFFIX = CarrierConfigManager.KEY_MMS_NAI_SUFFIX_STRING; 246 /** 247 * If true, show the cell broadcast (amber alert) in the SMS settings. Some carriers don't want 248 * this shown. (Boolean type) 249 */ 250 public static final String MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS = 251 CarrierConfigManager.KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL; 252 /** 253 * Whether the carrier MMSC supports charset field in Content-Type header. If this is false, 254 * then we don't add "charset" to "Content-Type" 255 */ 256 public static final String MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER = 257 CarrierConfigManager.KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL; 258 /** 259 * If true, add "Connection: close" header to MMS HTTP requests so the connection 260 * is immediately closed (disabling keep-alive). (Boolean type) 261 * @hide 262 */ 263 public static final String MMS_CONFIG_CLOSE_CONNECTION = 264 CarrierConfigManager.KEY_MMS_CLOSE_CONNECTION_BOOL; 265 266 /* 267 * Forwarded constants from SimDialogActivity. 268 */ 269 private static String DIALOG_TYPE_KEY = "dialog_type"; 270 private static final int SMS_PICK = 2; 271 272 /** 273 * Send a text based SMS. 274 * 275 * <p class="note"><strong>Note:</strong> Using this method requires that your app has the 276 * {@link android.Manifest.permission#SEND_SMS} permission.</p> 277 * 278 * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if 279 * <em>and only if</em> an app is not selected as the default SMS app, the system automatically 280 * writes messages sent using this method to the SMS Provider (the default SMS app is always 281 * responsible for writing its sent messages to the SMS Provider). For information about 282 * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p> 283 * 284 * 285 * @param destinationAddress the address to send the message to 286 * @param scAddress is the service center address or null to use 287 * the current default SMSC 288 * @param text the body of the message to send 289 * @param sentIntent if not NULL this <code>PendingIntent</code> is 290 * broadcast when the message is successfully sent, or failed. 291 * The result code will be <code>Activity.RESULT_OK</code> for success, 292 * or one of these errors:<br> 293 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 294 * <code>RESULT_ERROR_RADIO_OFF</code><br> 295 * <code>RESULT_ERROR_NULL_PDU</code><br> 296 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 297 * the extra "errorCode" containing a radio technology specific value, 298 * generally only useful for troubleshooting.<br> 299 * The per-application based SMS control checks sentIntent. If sentIntent 300 * is NULL the caller will be checked against all unknown applications, 301 * which cause smaller number of SMS to be sent in checking period. 302 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 303 * broadcast when the message is delivered to the recipient. The 304 * raw pdu of the status report is in the extended data ("pdu"). 305 * 306 * @throws IllegalArgumentException if destinationAddress or text are empty 307 */ 308 public void sendTextMessage( 309 String destinationAddress, String scAddress, String text, 310 PendingIntent sentIntent, PendingIntent deliveryIntent) { 311 sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, 312 true /* persistMessage*/); 313 } 314 315 private void sendTextMessageInternal(String destinationAddress, String scAddress, 316 String text, PendingIntent sentIntent, PendingIntent deliveryIntent, 317 boolean persistMessage) { 318 if (TextUtils.isEmpty(destinationAddress)) { 319 throw new IllegalArgumentException("Invalid destinationAddress"); 320 } 321 322 if (TextUtils.isEmpty(text)) { 323 throw new IllegalArgumentException("Invalid message body"); 324 } 325 326 try { 327 ISms iccISms = getISmsServiceOrThrow(); 328 iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), 329 destinationAddress, 330 scAddress, text, sentIntent, deliveryIntent, 331 persistMessage); 332 } catch (RemoteException ex) { 333 // ignore it 334 } 335 } 336 337 /** 338 * Send a text based SMS without writing it into the SMS Provider. 339 * 340 * <p>Requires Permission: 341 * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier 342 * privileges. 343 * </p> 344 * 345 * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent) 346 * @hide 347 */ 348 @SystemApi 349 public void sendTextMessageWithoutPersisting( 350 String destinationAddress, String scAddress, String text, 351 PendingIntent sentIntent, PendingIntent deliveryIntent) { 352 sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, 353 false /* persistMessage */); 354 } 355 356 /** 357 * A variant of {@link SmsManager#sendTextMessage} that allows self to be the caller. This is 358 * for internal use only. 359 * 360 * @param persistMessage whether to persist the sent message in the SMS app. the caller must be 361 * the Phone process if set to false. 362 * 363 * @hide 364 */ 365 public void sendTextMessageWithSelfPermissions( 366 String destinationAddress, String scAddress, String text, 367 PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage) { 368 if (TextUtils.isEmpty(destinationAddress)) { 369 throw new IllegalArgumentException("Invalid destinationAddress"); 370 } 371 372 if (TextUtils.isEmpty(text)) { 373 throw new IllegalArgumentException("Invalid message body"); 374 } 375 376 try { 377 ISms iccISms = getISmsServiceOrThrow(); 378 iccISms.sendTextForSubscriberWithSelfPermissions(getSubscriptionId(), 379 ActivityThread.currentPackageName(), 380 destinationAddress, 381 scAddress, text, sentIntent, deliveryIntent, persistMessage); 382 } catch (RemoteException ex) { 383 // ignore it 384 } 385 } 386 387 /** 388 * Inject an SMS PDU into the android application framework. 389 * 390 * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier 391 * privileges. @see android.telephony.TelephonyManager#hasCarrierPrivileges 392 * 393 * @param pdu is the byte array of pdu to be injected into android application framework 394 * @param format is the format of SMS pdu (3gpp or 3gpp2) 395 * @param receivedIntent if not NULL this <code>PendingIntent</code> is 396 * broadcast when the message is successfully received by the 397 * android application framework, or failed. This intent is broadcasted at 398 * the same time an SMS received from radio is acknowledged back. 399 * The result code will be <code>RESULT_SMS_HANDLED</code> for success, or 400 * <code>RESULT_SMS_GENERIC_ERROR</code> for error. 401 * 402 * @throws IllegalArgumentException if format is not one of 3gpp and 3gpp2. 403 */ 404 public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { 405 if (!format.equals(SmsMessage.FORMAT_3GPP) && !format.equals(SmsMessage.FORMAT_3GPP2)) { 406 // Format must be either 3gpp or 3gpp2. 407 throw new IllegalArgumentException( 408 "Invalid pdu format. format must be either 3gpp or 3gpp2"); 409 } 410 try { 411 ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 412 if (iccISms != null) { 413 iccISms.injectSmsPduForSubscriber( 414 getSubscriptionId(), pdu, format, receivedIntent); 415 } 416 } catch (RemoteException ex) { 417 // ignore it 418 } 419 } 420 421 /** 422 * Divide a message text into several fragments, none bigger than 423 * the maximum SMS message size. 424 * 425 * @param text the original message. Must not be null. 426 * @return an <code>ArrayList</code> of strings that, in order, 427 * comprise the original message 428 * 429 * @throws IllegalArgumentException if text is null 430 */ 431 public ArrayList<String> divideMessage(String text) { 432 if (null == text) { 433 throw new IllegalArgumentException("text is null"); 434 } 435 return SmsMessage.fragmentText(text); 436 } 437 438 /** 439 * Send a multi-part text based SMS. The callee should have already 440 * divided the message into correctly sized parts by calling 441 * <code>divideMessage</code>. 442 * 443 * <p class="note"><strong>Note:</strong> Using this method requires that your app has the 444 * {@link android.Manifest.permission#SEND_SMS} permission.</p> 445 * 446 * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if 447 * <em>and only if</em> an app is not selected as the default SMS app, the system automatically 448 * writes messages sent using this method to the SMS Provider (the default SMS app is always 449 * responsible for writing its sent messages to the SMS Provider). For information about 450 * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p> 451 * 452 * @param destinationAddress the address to send the message to 453 * @param scAddress is the service center address or null to use 454 * the current default SMSC 455 * @param parts an <code>ArrayList</code> of strings that, in order, 456 * comprise the original message 457 * @param sentIntents if not null, an <code>ArrayList</code> of 458 * <code>PendingIntent</code>s (one for each message part) that is 459 * broadcast when the corresponding message part has been sent. 460 * The result code will be <code>Activity.RESULT_OK</code> for success, 461 * or one of these errors:<br> 462 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 463 * <code>RESULT_ERROR_RADIO_OFF</code><br> 464 * <code>RESULT_ERROR_NULL_PDU</code><br> 465 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include 466 * the extra "errorCode" containing a radio technology specific value, 467 * generally only useful for troubleshooting.<br> 468 * The per-application based SMS control checks sentIntent. If sentIntent 469 * is NULL the caller will be checked against all unknown applications, 470 * which cause smaller number of SMS to be sent in checking period. 471 * @param deliveryIntents if not null, an <code>ArrayList</code> of 472 * <code>PendingIntent</code>s (one for each message part) that is 473 * broadcast when the corresponding message part has been delivered 474 * to the recipient. The raw pdu of the status report is in the 475 * extended data ("pdu"). 476 * 477 * @throws IllegalArgumentException if destinationAddress or data are empty 478 */ 479 public void sendMultipartTextMessage( 480 String destinationAddress, String scAddress, ArrayList<String> parts, 481 ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { 482 sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, 483 deliveryIntents, true /* persistMessage*/); 484 } 485 486 private void sendMultipartTextMessageInternal( 487 String destinationAddress, String scAddress, List<String> parts, 488 List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents, 489 boolean persistMessage) { 490 if (TextUtils.isEmpty(destinationAddress)) { 491 throw new IllegalArgumentException("Invalid destinationAddress"); 492 } 493 if (parts == null || parts.size() < 1) { 494 throw new IllegalArgumentException("Invalid message body"); 495 } 496 497 if (parts.size() > 1) { 498 try { 499 ISms iccISms = getISmsServiceOrThrow(); 500 iccISms.sendMultipartTextForSubscriber(getSubscriptionId(), 501 ActivityThread.currentPackageName(), 502 destinationAddress, scAddress, parts, 503 sentIntents, deliveryIntents, persistMessage); 504 } catch (RemoteException ex) { 505 // ignore it 506 } 507 } else { 508 PendingIntent sentIntent = null; 509 PendingIntent deliveryIntent = null; 510 if (sentIntents != null && sentIntents.size() > 0) { 511 sentIntent = sentIntents.get(0); 512 } 513 if (deliveryIntents != null && deliveryIntents.size() > 0) { 514 deliveryIntent = deliveryIntents.get(0); 515 } 516 sendTextMessage(destinationAddress, scAddress, parts.get(0), 517 sentIntent, deliveryIntent); 518 } 519 } 520 521 /** 522 * Send a multi-part text based SMS without writing it into the SMS Provider. 523 * 524 * <p>Requires Permission: 525 * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier 526 * privileges. 527 * </p> 528 * 529 * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList) 530 * @hide 531 **/ 532 @SystemApi 533 public void sendMultipartTextMessageWithoutPersisting( 534 String destinationAddress, String scAddress, List<String> parts, 535 List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { 536 sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, 537 deliveryIntents, false /* persistMessage*/); 538 } 539 540 /** 541 * Send a data based SMS to a specific application port. 542 * 543 * <p class="note"><strong>Note:</strong> Using this method requires that your app has the 544 * {@link android.Manifest.permission#SEND_SMS} permission.</p> 545 * 546 * @param destinationAddress the address to send the message to 547 * @param scAddress is the service center address or null to use 548 * the current default SMSC 549 * @param destinationPort the port to deliver the message to 550 * @param data the body of the message to send 551 * @param sentIntent if not NULL this <code>PendingIntent</code> is 552 * broadcast when the message is successfully sent, or failed. 553 * The result code will be <code>Activity.RESULT_OK</code> for success, 554 * or one of these errors:<br> 555 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 556 * <code>RESULT_ERROR_RADIO_OFF</code><br> 557 * <code>RESULT_ERROR_NULL_PDU</code><br> 558 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 559 * the extra "errorCode" containing a radio technology specific value, 560 * generally only useful for troubleshooting.<br> 561 * The per-application based SMS control checks sentIntent. If sentIntent 562 * is NULL the caller will be checked against all unknown applications, 563 * which cause smaller number of SMS to be sent in checking period. 564 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 565 * broadcast when the message is delivered to the recipient. The 566 * raw pdu of the status report is in the extended data ("pdu"). 567 * 568 * @throws IllegalArgumentException if destinationAddress or data are empty 569 */ 570 public void sendDataMessage( 571 String destinationAddress, String scAddress, short destinationPort, 572 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 573 if (TextUtils.isEmpty(destinationAddress)) { 574 throw new IllegalArgumentException("Invalid destinationAddress"); 575 } 576 577 if (data == null || data.length == 0) { 578 throw new IllegalArgumentException("Invalid message data"); 579 } 580 581 try { 582 ISms iccISms = getISmsServiceOrThrow(); 583 iccISms.sendDataForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), 584 destinationAddress, scAddress, destinationPort & 0xFFFF, 585 data, sentIntent, deliveryIntent); 586 } catch (RemoteException ex) { 587 // ignore it 588 } 589 } 590 591 /** 592 * A variant of {@link SmsManager#sendDataMessage} that allows self to be the caller. This is 593 * for internal use only. 594 * 595 * @hide 596 */ 597 public void sendDataMessageWithSelfPermissions( 598 String destinationAddress, String scAddress, short destinationPort, 599 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 600 if (TextUtils.isEmpty(destinationAddress)) { 601 throw new IllegalArgumentException("Invalid destinationAddress"); 602 } 603 604 if (data == null || data.length == 0) { 605 throw new IllegalArgumentException("Invalid message data"); 606 } 607 608 try { 609 ISms iccISms = getISmsServiceOrThrow(); 610 iccISms.sendDataForSubscriberWithSelfPermissions(getSubscriptionId(), 611 ActivityThread.currentPackageName(), destinationAddress, scAddress, 612 destinationPort & 0xFFFF, data, sentIntent, deliveryIntent); 613 } catch (RemoteException ex) { 614 // ignore it 615 } 616 } 617 618 619 620 /** 621 * Get the SmsManager associated with the default subscription id. The instance will always be 622 * associated with the default subscription id, even if the default subscription id is changed. 623 * 624 * @return the SmsManager associated with the default subscription id 625 */ 626 public static SmsManager getDefault() { 627 return sInstance; 628 } 629 630 /** 631 * Get the the instance of the SmsManager associated with a particular subscription id 632 * 633 * @param subId an SMS subscription id, typically accessed using 634 * {@link android.telephony.SubscriptionManager} 635 * @return the instance of the SmsManager associated with subId 636 */ 637 public static SmsManager getSmsManagerForSubscriptionId(int subId) { 638 // TODO(shri): Add javadoc link once SubscriptionManager is made public api 639 synchronized(sLockObject) { 640 SmsManager smsManager = sSubInstances.get(subId); 641 if (smsManager == null) { 642 smsManager = new SmsManager(subId); 643 sSubInstances.put(subId, smsManager); 644 } 645 return smsManager; 646 } 647 } 648 649 private SmsManager(int subId) { 650 mSubId = subId; 651 } 652 653 /** 654 * Get the associated subscription id. If the instance was returned by {@link #getDefault()}, 655 * then this method may return different values at different points in time (if the user 656 * changes the default subscription id). It will return < 0 if the default subscription id 657 * cannot be determined. 658 * 659 * Additionally, to support legacy applications that are not multi-SIM aware, 660 * if the following are true: 661 * - We are using a multi-SIM device 662 * - A default SMS SIM has not been selected 663 * - At least one SIM subscription is available 664 * then ask the user to set the default SMS SIM. 665 * 666 * @return associated subscription id 667 */ 668 public int getSubscriptionId() { 669 final int subId = (mSubId == DEFAULT_SUBSCRIPTION_ID) 670 ? getDefaultSmsSubscriptionId() : mSubId; 671 boolean isSmsSimPickActivityNeeded = false; 672 final Context context = ActivityThread.currentApplication().getApplicationContext(); 673 try { 674 ISms iccISms = getISmsService(); 675 if (iccISms != null) { 676 isSmsSimPickActivityNeeded = iccISms.isSmsSimPickActivityNeeded(subId); 677 } 678 } catch (RemoteException ex) { 679 Log.e(TAG, "Exception in getSubscriptionId"); 680 } 681 682 if (isSmsSimPickActivityNeeded) { 683 Log.d(TAG, "getSubscriptionId isSmsSimPickActivityNeeded is true"); 684 // ask the user for a default SMS SIM. 685 Intent intent = new Intent(); 686 intent.setClassName("com.android.settings", 687 "com.android.settings.sim.SimDialogActivity"); 688 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 689 intent.putExtra(DIALOG_TYPE_KEY, SMS_PICK); 690 try { 691 context.startActivity(intent); 692 } catch (ActivityNotFoundException anfe) { 693 // If Settings is not installed, only log the error as we do not want to break 694 // legacy applications. 695 Log.e(TAG, "Unable to launch Settings application."); 696 } 697 } 698 699 return subId; 700 } 701 702 /** 703 * Returns the ISms service, or throws an UnsupportedOperationException if 704 * the service does not exist. 705 */ 706 private static ISms getISmsServiceOrThrow() { 707 ISms iccISms = getISmsService(); 708 if (iccISms == null) { 709 throw new UnsupportedOperationException("Sms is not supported"); 710 } 711 return iccISms; 712 } 713 714 private static ISms getISmsService() { 715 return ISms.Stub.asInterface(ServiceManager.getService("isms")); 716 } 717 718 /** 719 * Copy a raw SMS PDU to the ICC. 720 * ICC (Integrated Circuit Card) is the card of the device. 721 * For example, this can be the SIM or USIM for GSM. 722 * 723 * @param smsc the SMSC for this message, or NULL for the default SMSC 724 * @param pdu the raw PDU to store 725 * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD, 726 * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT) 727 * @return true for success 728 * 729 * @throws IllegalArgumentException if pdu is NULL 730 * {@hide} 731 */ 732 public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) { 733 boolean success = false; 734 735 if (null == pdu) { 736 throw new IllegalArgumentException("pdu is NULL"); 737 } 738 try { 739 ISms iccISms = getISmsService(); 740 if (iccISms != null) { 741 success = iccISms.copyMessageToIccEfForSubscriber(getSubscriptionId(), 742 ActivityThread.currentPackageName(), 743 status, pdu, smsc); 744 } 745 } catch (RemoteException ex) { 746 // ignore it 747 } 748 749 return success; 750 } 751 752 /** 753 * Delete the specified message from the ICC. 754 * ICC (Integrated Circuit Card) is the card of the device. 755 * For example, this can be the SIM or USIM for GSM. 756 * 757 * @param messageIndex is the record index of the message on ICC 758 * @return true for success 759 * 760 * {@hide} 761 */ 762 public boolean 763 deleteMessageFromIcc(int messageIndex) { 764 boolean success = false; 765 byte[] pdu = new byte[SMS_RECORD_LENGTH-1]; 766 Arrays.fill(pdu, (byte)0xff); 767 768 try { 769 ISms iccISms = getISmsService(); 770 if (iccISms != null) { 771 success = iccISms.updateMessageOnIccEfForSubscriber(getSubscriptionId(), 772 ActivityThread.currentPackageName(), 773 messageIndex, STATUS_ON_ICC_FREE, pdu); 774 } 775 } catch (RemoteException ex) { 776 // ignore it 777 } 778 779 return success; 780 } 781 782 /** 783 * Update the specified message on the ICC. 784 * ICC (Integrated Circuit Card) is the card of the device. 785 * For example, this can be the SIM or USIM for GSM. 786 * 787 * @param messageIndex record index of message to update 788 * @param newStatus new message status (STATUS_ON_ICC_READ, 789 * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT, 790 * STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE) 791 * @param pdu the raw PDU to store 792 * @return true for success 793 * 794 * {@hide} 795 */ 796 public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) { 797 boolean success = false; 798 799 try { 800 ISms iccISms = getISmsService(); 801 if (iccISms != null) { 802 success = iccISms.updateMessageOnIccEfForSubscriber(getSubscriptionId(), 803 ActivityThread.currentPackageName(), 804 messageIndex, newStatus, pdu); 805 } 806 } catch (RemoteException ex) { 807 // ignore it 808 } 809 810 return success; 811 } 812 813 /** 814 * Retrieves all messages currently stored on ICC. 815 * ICC (Integrated Circuit Card) is the card of the device. 816 * For example, this can be the SIM or USIM for GSM. 817 * 818 * @return <code>ArrayList</code> of <code>SmsMessage</code> objects 819 * 820 * {@hide} 821 */ 822 public ArrayList<SmsMessage> getAllMessagesFromIcc() { 823 List<SmsRawData> records = null; 824 825 try { 826 ISms iccISms = getISmsService(); 827 if (iccISms != null) { 828 records = iccISms.getAllMessagesFromIccEfForSubscriber( 829 getSubscriptionId(), 830 ActivityThread.currentPackageName()); 831 } 832 } catch (RemoteException ex) { 833 // ignore it 834 } 835 836 return createMessageListFromRawRecords(records); 837 } 838 839 /** 840 * Enable reception of cell broadcast (SMS-CB) messages with the given 841 * message identifier and RAN type. The RAN type specify this message ID 842 * belong to 3GPP (GSM) or 3GPP2(CDMA).Note that if two different clients 843 * enable the same message identifier, they must both disable it for the device to stop 844 * receiving those messages. All received messages will be broadcast in an 845 * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED". 846 * Note: This call is blocking, callers may want to avoid calling it from 847 * the main thread of an application. 848 * 849 * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP) 850 * or C.R1001-G (3GPP2) 851 * @param ranType as defined in class SmsManager, the value can be one of these: 852 * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM 853 * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA 854 * @return true if successful, false otherwise 855 * @see #disableCellBroadcast(int, int) 856 * 857 * {@hide} 858 */ 859 public boolean enableCellBroadcast(int messageIdentifier, int ranType) { 860 boolean success = false; 861 862 try { 863 ISms iccISms = getISmsService(); 864 if (iccISms != null) { 865 success = iccISms.enableCellBroadcastForSubscriber( 866 getSubscriptionId(), messageIdentifier, ranType); 867 } 868 } catch (RemoteException ex) { 869 // ignore it 870 } 871 872 return success; 873 } 874 875 /** 876 * Disable reception of cell broadcast (SMS-CB) messages with the given 877 * message identifier and RAN type. The RAN type specify this message ID 878 * belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients 879 * enable the same message identifier, they must both disable it for the 880 * device to stop receiving those messages. 881 * Note: This call is blocking, callers may want to avoid calling it from 882 * the main thread of an application. 883 * 884 * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP) 885 * or C.R1001-G (3GPP2) 886 * @param ranType as defined in class SmsManager, the value can be one of these: 887 * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM 888 * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA 889 * @return true if successful, false otherwise 890 * 891 * @see #enableCellBroadcast(int, int) 892 * 893 * {@hide} 894 */ 895 public boolean disableCellBroadcast(int messageIdentifier, int ranType) { 896 boolean success = false; 897 898 try { 899 ISms iccISms = getISmsService(); 900 if (iccISms != null) { 901 success = iccISms.disableCellBroadcastForSubscriber( 902 getSubscriptionId(), messageIdentifier, ranType); 903 } 904 } catch (RemoteException ex) { 905 // ignore it 906 } 907 908 return success; 909 } 910 911 /** 912 * Enable reception of cell broadcast (SMS-CB) messages with the given 913 * message identifier range and RAN type. The RAN type specify this message ID 914 * belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients enable 915 * the same message identifier, they must both disable it for the device to stop 916 * receiving those messages. All received messages will be broadcast in an 917 * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED". 918 * Note: This call is blocking, callers may want to avoid calling it from 919 * the main thread of an application. 920 * 921 * @param startMessageId first message identifier as specified in TS 23.041 (3GPP) 922 * or C.R1001-G (3GPP2) 923 * @param endMessageId last message identifier as specified in TS 23.041 (3GPP) 924 * or C.R1001-G (3GPP2) 925 * @param ranType as defined in class SmsManager, the value can be one of these: 926 * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM 927 * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA 928 * @return true if successful, false otherwise 929 * @see #disableCellBroadcastRange(int, int, int) 930 * 931 * @throws IllegalArgumentException if endMessageId < startMessageId 932 * {@hide} 933 */ 934 public boolean enableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) { 935 boolean success = false; 936 937 if (endMessageId < startMessageId) { 938 throw new IllegalArgumentException("endMessageId < startMessageId"); 939 } 940 try { 941 ISms iccISms = getISmsService(); 942 if (iccISms != null) { 943 success = iccISms.enableCellBroadcastRangeForSubscriber(getSubscriptionId(), 944 startMessageId, endMessageId, ranType); 945 } 946 } catch (RemoteException ex) { 947 // ignore it 948 } 949 950 return success; 951 } 952 953 /** 954 * Disable reception of cell broadcast (SMS-CB) messages with the given 955 * message identifier range and RAN type. The RAN type specify this message 956 * ID range belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different 957 * clients enable the same message identifier, they must both disable it for 958 * the device to stop receiving those messages. 959 * Note: This call is blocking, callers may want to avoid calling it from 960 * the main thread of an application. 961 * 962 * @param startMessageId first message identifier as specified in TS 23.041 (3GPP) 963 * or C.R1001-G (3GPP2) 964 * @param endMessageId last message identifier as specified in TS 23.041 (3GPP) 965 * or C.R1001-G (3GPP2) 966 * @param ranType as defined in class SmsManager, the value can be one of these: 967 * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM 968 * android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA 969 * @return true if successful, false otherwise 970 * 971 * @see #enableCellBroadcastRange(int, int, int) 972 * 973 * @throws IllegalArgumentException if endMessageId < startMessageId 974 * {@hide} 975 */ 976 public boolean disableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) { 977 boolean success = false; 978 979 if (endMessageId < startMessageId) { 980 throw new IllegalArgumentException("endMessageId < startMessageId"); 981 } 982 try { 983 ISms iccISms = getISmsService(); 984 if (iccISms != null) { 985 success = iccISms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(), 986 startMessageId, endMessageId, ranType); 987 } 988 } catch (RemoteException ex) { 989 // ignore it 990 } 991 992 return success; 993 } 994 995 /** 996 * Create a list of <code>SmsMessage</code>s from a list of RawSmsData 997 * records returned by <code>getAllMessagesFromIcc()</code> 998 * 999 * @param records SMS EF records, returned by 1000 * <code>getAllMessagesFromIcc</code> 1001 * @return <code>ArrayList</code> of <code>SmsMessage</code> objects. 1002 */ 1003 private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) { 1004 ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>(); 1005 if (records != null) { 1006 int count = records.size(); 1007 for (int i = 0; i < count; i++) { 1008 SmsRawData data = records.get(i); 1009 // List contains all records, including "free" records (null) 1010 if (data != null) { 1011 SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes()); 1012 if (sms != null) { 1013 messages.add(sms); 1014 } 1015 } 1016 } 1017 } 1018 return messages; 1019 } 1020 1021 /** 1022 * SMS over IMS is supported if IMS is registered and SMS is supported 1023 * on IMS. 1024 * 1025 * @return true if SMS over IMS is supported, false otherwise 1026 * 1027 * @see #getImsSmsFormat() 1028 * 1029 * @hide 1030 */ 1031 public boolean isImsSmsSupported() { 1032 boolean boSupported = false; 1033 try { 1034 ISms iccISms = getISmsService(); 1035 if (iccISms != null) { 1036 boSupported = iccISms.isImsSmsSupportedForSubscriber(getSubscriptionId()); 1037 } 1038 } catch (RemoteException ex) { 1039 // ignore it 1040 } 1041 return boSupported; 1042 } 1043 1044 /** 1045 * Gets SMS format supported on IMS. SMS over IMS format is 1046 * either 3GPP or 3GPP2. 1047 * 1048 * @return SmsMessage.FORMAT_3GPP, 1049 * SmsMessage.FORMAT_3GPP2 1050 * or SmsMessage.FORMAT_UNKNOWN 1051 * 1052 * @see #isImsSmsSupported() 1053 * 1054 * @hide 1055 */ 1056 public String getImsSmsFormat() { 1057 String format = com.android.internal.telephony.SmsConstants.FORMAT_UNKNOWN; 1058 try { 1059 ISms iccISms = getISmsService(); 1060 if (iccISms != null) { 1061 format = iccISms.getImsSmsFormatForSubscriber(getSubscriptionId()); 1062 } 1063 } catch (RemoteException ex) { 1064 // ignore it 1065 } 1066 return format; 1067 } 1068 1069 /** 1070 * Get default sms subscription id 1071 * 1072 * @return the default SMS subscription id 1073 */ 1074 public static int getDefaultSmsSubscriptionId() { 1075 ISms iccISms = null; 1076 try { 1077 iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 1078 return iccISms.getPreferredSmsSubscription(); 1079 } catch (RemoteException ex) { 1080 return -1; 1081 } catch (NullPointerException ex) { 1082 return -1; 1083 } 1084 } 1085 1086 /** 1087 * Get SMS prompt property, enabled or not 1088 * 1089 * @return true if enabled, false otherwise 1090 * @hide 1091 */ 1092 public boolean isSMSPromptEnabled() { 1093 ISms iccISms = null; 1094 try { 1095 iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); 1096 return iccISms.isSMSPromptEnabled(); 1097 } catch (RemoteException ex) { 1098 return false; 1099 } catch (NullPointerException ex) { 1100 return false; 1101 } 1102 } 1103 1104 // see SmsMessage.getStatusOnIcc 1105 1106 /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 1107 static public final int STATUS_ON_ICC_FREE = 0; 1108 1109 /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 1110 static public final int STATUS_ON_ICC_READ = 1; 1111 1112 /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 1113 static public final int STATUS_ON_ICC_UNREAD = 3; 1114 1115 /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 1116 static public final int STATUS_ON_ICC_SENT = 5; 1117 1118 /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */ 1119 static public final int STATUS_ON_ICC_UNSENT = 7; 1120 1121 // SMS send failure result codes 1122 1123 /** Generic failure cause */ 1124 static public final int RESULT_ERROR_GENERIC_FAILURE = 1; 1125 /** Failed because radio was explicitly turned off */ 1126 static public final int RESULT_ERROR_RADIO_OFF = 2; 1127 /** Failed because no pdu provided */ 1128 static public final int RESULT_ERROR_NULL_PDU = 3; 1129 /** Failed because service is currently unavailable */ 1130 static public final int RESULT_ERROR_NO_SERVICE = 4; 1131 /** Failed because we reached the sending queue limit. {@hide} */ 1132 static public final int RESULT_ERROR_LIMIT_EXCEEDED = 5; 1133 /** Failed because FDN is enabled. {@hide} */ 1134 static public final int RESULT_ERROR_FDN_CHECK_FAILURE = 6; 1135 1136 static private final String PHONE_PACKAGE_NAME = "com.android.phone"; 1137 1138 /** 1139 * Send an MMS message 1140 * 1141 * @param context application context 1142 * @param contentUri the content Uri from which the message pdu will be read 1143 * @param locationUrl the optional location url where message should be sent to 1144 * @param configOverrides the carrier-specific messaging configuration values to override for 1145 * sending the message. 1146 * @param sentIntent if not NULL this <code>PendingIntent</code> is 1147 * broadcast when the message is successfully sent, or failed 1148 * @throws IllegalArgumentException if contentUri is empty 1149 */ 1150 public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, 1151 Bundle configOverrides, PendingIntent sentIntent) { 1152 if (contentUri == null) { 1153 throw new IllegalArgumentException("Uri contentUri null"); 1154 } 1155 try { 1156 final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1157 if (iMms == null) { 1158 return; 1159 } 1160 1161 iMms.sendMessage(getSubscriptionId(), ActivityThread.currentPackageName(), contentUri, 1162 locationUrl, configOverrides, sentIntent); 1163 } catch (RemoteException e) { 1164 // Ignore it 1165 } 1166 } 1167 1168 /** 1169 * Download an MMS message from carrier by a given location URL 1170 * 1171 * @param context application context 1172 * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained 1173 * from the MMS WAP push notification 1174 * @param contentUri the content uri to which the downloaded pdu will be written 1175 * @param configOverrides the carrier-specific messaging configuration values to override for 1176 * downloading the message. 1177 * @param downloadedIntent if not NULL this <code>PendingIntent</code> is 1178 * broadcast when the message is downloaded, or the download is failed 1179 * @throws IllegalArgumentException if locationUrl or contentUri is empty 1180 */ 1181 public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri, 1182 Bundle configOverrides, PendingIntent downloadedIntent) { 1183 if (TextUtils.isEmpty(locationUrl)) { 1184 throw new IllegalArgumentException("Empty MMS location URL"); 1185 } 1186 if (contentUri == null) { 1187 throw new IllegalArgumentException("Uri contentUri null"); 1188 } 1189 try { 1190 final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1191 if (iMms == null) { 1192 return; 1193 } 1194 iMms.downloadMessage( 1195 getSubscriptionId(), ActivityThread.currentPackageName(), locationUrl, 1196 contentUri, configOverrides, downloadedIntent); 1197 } catch (RemoteException e) { 1198 // Ignore it 1199 } 1200 } 1201 1202 // MMS send/download failure result codes 1203 public static final int MMS_ERROR_UNSPECIFIED = 1; 1204 public static final int MMS_ERROR_INVALID_APN = 2; 1205 public static final int MMS_ERROR_UNABLE_CONNECT_MMS = 3; 1206 public static final int MMS_ERROR_HTTP_FAILURE = 4; 1207 public static final int MMS_ERROR_IO_ERROR = 5; 1208 public static final int MMS_ERROR_RETRY = 6; 1209 public static final int MMS_ERROR_CONFIGURATION_ERROR = 7; 1210 public static final int MMS_ERROR_NO_DATA_NETWORK = 8; 1211 1212 /** Intent extra name for MMS sending result data in byte array type */ 1213 public static final String EXTRA_MMS_DATA = "android.telephony.extra.MMS_DATA"; 1214 /** Intent extra name for HTTP status code for MMS HTTP failure in integer type */ 1215 public static final String EXTRA_MMS_HTTP_STATUS = "android.telephony.extra.MMS_HTTP_STATUS"; 1216 1217 /** 1218 * Import a text message into system's SMS store 1219 * 1220 * Only default SMS apps can import SMS 1221 * 1222 * @param address the destination(source) address of the sent(received) message 1223 * @param type the type of the message 1224 * @param text the message text 1225 * @param timestampMillis the message timestamp in milliseconds 1226 * @param seen if the message is seen 1227 * @param read if the message is read 1228 * @return the message URI, null if failed 1229 * @hide 1230 */ 1231 public Uri importTextMessage(String address, int type, String text, long timestampMillis, 1232 boolean seen, boolean read) { 1233 try { 1234 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1235 if (iMms != null) { 1236 return iMms.importTextMessage(ActivityThread.currentPackageName(), 1237 address, type, text, timestampMillis, seen, read); 1238 } 1239 } catch (RemoteException ex) { 1240 // ignore it 1241 } 1242 return null; 1243 } 1244 1245 /** Represents the received SMS message for importing {@hide} */ 1246 public static final int SMS_TYPE_INCOMING = 0; 1247 /** Represents the sent SMS message for importing {@hide} */ 1248 public static final int SMS_TYPE_OUTGOING = 1; 1249 1250 /** 1251 * Import a multimedia message into system's MMS store. Only the following PDU type is 1252 * supported: Retrieve.conf, Send.req, Notification.ind, Delivery.ind, Read-Orig.ind 1253 * 1254 * Only default SMS apps can import MMS 1255 * 1256 * @param contentUri the content uri from which to read the PDU of the message to import 1257 * @param messageId the optional message id. Use null if not specifying 1258 * @param timestampSecs the optional message timestamp. Use -1 if not specifying 1259 * @param seen if the message is seen 1260 * @param read if the message is read 1261 * @return the message URI, null if failed 1262 * @throws IllegalArgumentException if pdu is empty 1263 * {@hide} 1264 */ 1265 public Uri importMultimediaMessage(Uri contentUri, String messageId, long timestampSecs, 1266 boolean seen, boolean read) { 1267 if (contentUri == null) { 1268 throw new IllegalArgumentException("Uri contentUri null"); 1269 } 1270 try { 1271 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1272 if (iMms != null) { 1273 return iMms.importMultimediaMessage(ActivityThread.currentPackageName(), 1274 contentUri, messageId, timestampSecs, seen, read); 1275 } 1276 } catch (RemoteException ex) { 1277 // ignore it 1278 } 1279 return null; 1280 } 1281 1282 /** 1283 * Delete a system stored SMS or MMS message 1284 * 1285 * Only default SMS apps can delete system stored SMS and MMS messages 1286 * 1287 * @param messageUri the URI of the stored message 1288 * @return true if deletion is successful, false otherwise 1289 * @throws IllegalArgumentException if messageUri is empty 1290 * {@hide} 1291 */ 1292 public boolean deleteStoredMessage(Uri messageUri) { 1293 if (messageUri == null) { 1294 throw new IllegalArgumentException("Empty message URI"); 1295 } 1296 try { 1297 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1298 if (iMms != null) { 1299 return iMms.deleteStoredMessage(ActivityThread.currentPackageName(), messageUri); 1300 } 1301 } catch (RemoteException ex) { 1302 // ignore it 1303 } 1304 return false; 1305 } 1306 1307 /** 1308 * Delete a system stored SMS or MMS thread 1309 * 1310 * Only default SMS apps can delete system stored SMS and MMS conversations 1311 * 1312 * @param conversationId the ID of the message conversation 1313 * @return true if deletion is successful, false otherwise 1314 * {@hide} 1315 */ 1316 public boolean deleteStoredConversation(long conversationId) { 1317 try { 1318 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1319 if (iMms != null) { 1320 return iMms.deleteStoredConversation( 1321 ActivityThread.currentPackageName(), conversationId); 1322 } 1323 } catch (RemoteException ex) { 1324 // ignore it 1325 } 1326 return false; 1327 } 1328 1329 /** 1330 * Update the status properties of a system stored SMS or MMS message, e.g. 1331 * the read status of a message, etc. 1332 * 1333 * @param messageUri the URI of the stored message 1334 * @param statusValues a list of status properties in key-value pairs to update 1335 * @return true if update is successful, false otherwise 1336 * @throws IllegalArgumentException if messageUri is empty 1337 * {@hide} 1338 */ 1339 public boolean updateStoredMessageStatus(Uri messageUri, ContentValues statusValues) { 1340 if (messageUri == null) { 1341 throw new IllegalArgumentException("Empty message URI"); 1342 } 1343 try { 1344 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1345 if (iMms != null) { 1346 return iMms.updateStoredMessageStatus(ActivityThread.currentPackageName(), 1347 messageUri, statusValues); 1348 } 1349 } catch (RemoteException ex) { 1350 // ignore it 1351 } 1352 return false; 1353 } 1354 1355 /** Message status property: whether the message has been seen. 1 means seen, 0 not {@hide} */ 1356 public static final String MESSAGE_STATUS_SEEN = "seen"; 1357 /** Message status property: whether the message has been read. 1 means read, 0 not {@hide} */ 1358 public static final String MESSAGE_STATUS_READ = "read"; 1359 1360 /** 1361 * Archive or unarchive a stored conversation 1362 * 1363 * @param conversationId the ID of the message conversation 1364 * @param archived true to archive the conversation, false to unarchive 1365 * @return true if update is successful, false otherwise 1366 * {@hide} 1367 */ 1368 public boolean archiveStoredConversation(long conversationId, boolean archived) { 1369 try { 1370 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1371 if (iMms != null) { 1372 return iMms.archiveStoredConversation(ActivityThread.currentPackageName(), 1373 conversationId, archived); 1374 } 1375 } catch (RemoteException ex) { 1376 // ignore it 1377 } 1378 return false; 1379 } 1380 1381 /** 1382 * Add a text message draft to system SMS store 1383 * 1384 * Only default SMS apps can add SMS draft 1385 * 1386 * @param address the destination address of message 1387 * @param text the body of the message to send 1388 * @return the URI of the stored draft message 1389 * {@hide} 1390 */ 1391 public Uri addTextMessageDraft(String address, String text) { 1392 try { 1393 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1394 if (iMms != null) { 1395 return iMms.addTextMessageDraft(ActivityThread.currentPackageName(), address, text); 1396 } 1397 } catch (RemoteException ex) { 1398 // ignore it 1399 } 1400 return null; 1401 } 1402 1403 /** 1404 * Add a multimedia message draft to system MMS store 1405 * 1406 * Only default SMS apps can add MMS draft 1407 * 1408 * @param contentUri the content uri from which to read the PDU data of the draft MMS 1409 * @return the URI of the stored draft message 1410 * @throws IllegalArgumentException if pdu is empty 1411 * {@hide} 1412 */ 1413 public Uri addMultimediaMessageDraft(Uri contentUri) { 1414 if (contentUri == null) { 1415 throw new IllegalArgumentException("Uri contentUri null"); 1416 } 1417 try { 1418 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1419 if (iMms != null) { 1420 return iMms.addMultimediaMessageDraft(ActivityThread.currentPackageName(), 1421 contentUri); 1422 } 1423 } catch (RemoteException ex) { 1424 // ignore it 1425 } 1426 return null; 1427 } 1428 1429 /** 1430 * Send a system stored text message. 1431 * 1432 * You can only send a failed text message or a draft text message. 1433 * 1434 * @param messageUri the URI of the stored message 1435 * @param scAddress is the service center address or null to use the current default SMSC 1436 * @param sentIntent if not NULL this <code>PendingIntent</code> is 1437 * broadcast when the message is successfully sent, or failed. 1438 * The result code will be <code>Activity.RESULT_OK</code> for success, 1439 * or one of these errors:<br> 1440 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 1441 * <code>RESULT_ERROR_RADIO_OFF</code><br> 1442 * <code>RESULT_ERROR_NULL_PDU</code><br> 1443 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 1444 * the extra "errorCode" containing a radio technology specific value, 1445 * generally only useful for troubleshooting.<br> 1446 * The per-application based SMS control checks sentIntent. If sentIntent 1447 * is NULL the caller will be checked against all unknown applications, 1448 * which cause smaller number of SMS to be sent in checking period. 1449 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 1450 * broadcast when the message is delivered to the recipient. The 1451 * raw pdu of the status report is in the extended data ("pdu"). 1452 * 1453 * @throws IllegalArgumentException if messageUri is empty 1454 * {@hide} 1455 */ 1456 public void sendStoredTextMessage(Uri messageUri, String scAddress, PendingIntent sentIntent, 1457 PendingIntent deliveryIntent) { 1458 if (messageUri == null) { 1459 throw new IllegalArgumentException("Empty message URI"); 1460 } 1461 try { 1462 ISms iccISms = getISmsServiceOrThrow(); 1463 iccISms.sendStoredText( 1464 getSubscriptionId(), ActivityThread.currentPackageName(), messageUri, 1465 scAddress, sentIntent, deliveryIntent); 1466 } catch (RemoteException ex) { 1467 // ignore it 1468 } 1469 } 1470 1471 /** 1472 * Send a system stored multi-part text message. 1473 * 1474 * You can only send a failed text message or a draft text message. 1475 * The provided <code>PendingIntent</code> lists should match the part number of the 1476 * divided text of the stored message by using <code>divideMessage</code> 1477 * 1478 * @param messageUri the URI of the stored message 1479 * @param scAddress is the service center address or null to use 1480 * the current default SMSC 1481 * @param sentIntents if not null, an <code>ArrayList</code> of 1482 * <code>PendingIntent</code>s (one for each message part) that is 1483 * broadcast when the corresponding message part has been sent. 1484 * The result code will be <code>Activity.RESULT_OK</code> for success, 1485 * or one of these errors:<br> 1486 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 1487 * <code>RESULT_ERROR_RADIO_OFF</code><br> 1488 * <code>RESULT_ERROR_NULL_PDU</code><br> 1489 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include 1490 * the extra "errorCode" containing a radio technology specific value, 1491 * generally only useful for troubleshooting.<br> 1492 * The per-application based SMS control checks sentIntent. If sentIntent 1493 * is NULL the caller will be checked against all unknown applications, 1494 * which cause smaller number of SMS to be sent in checking period. 1495 * @param deliveryIntents if not null, an <code>ArrayList</code> of 1496 * <code>PendingIntent</code>s (one for each message part) that is 1497 * broadcast when the corresponding message part has been delivered 1498 * to the recipient. The raw pdu of the status report is in the 1499 * extended data ("pdu"). 1500 * 1501 * @throws IllegalArgumentException if messageUri is empty 1502 * {@hide} 1503 */ 1504 public void sendStoredMultipartTextMessage(Uri messageUri, String scAddress, 1505 ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { 1506 if (messageUri == null) { 1507 throw new IllegalArgumentException("Empty message URI"); 1508 } 1509 try { 1510 ISms iccISms = getISmsServiceOrThrow(); 1511 iccISms.sendStoredMultipartText( 1512 getSubscriptionId(), ActivityThread.currentPackageName(), messageUri, 1513 scAddress, sentIntents, deliveryIntents); 1514 } catch (RemoteException ex) { 1515 // ignore it 1516 } 1517 } 1518 1519 /** 1520 * Send a system stored MMS message 1521 * 1522 * This is used for sending a previously sent, but failed-to-send, message or 1523 * for sending a text message that has been stored as a draft. 1524 * 1525 * @param messageUri the URI of the stored message 1526 * @param configOverrides the carrier-specific messaging configuration values to override for 1527 * sending the message. 1528 * @param sentIntent if not NULL this <code>PendingIntent</code> is 1529 * broadcast when the message is successfully sent, or failed 1530 * @throws IllegalArgumentException if messageUri is empty 1531 * {@hide} 1532 */ 1533 public void sendStoredMultimediaMessage(Uri messageUri, Bundle configOverrides, 1534 PendingIntent sentIntent) { 1535 if (messageUri == null) { 1536 throw new IllegalArgumentException("Empty message URI"); 1537 } 1538 try { 1539 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1540 if (iMms != null) { 1541 iMms.sendStoredMessage( 1542 getSubscriptionId(), ActivityThread.currentPackageName(), messageUri, 1543 configOverrides, sentIntent); 1544 } 1545 } catch (RemoteException ex) { 1546 // ignore it 1547 } 1548 } 1549 1550 /** 1551 * Turns on/off the flag to automatically write sent/received SMS/MMS messages into system 1552 * 1553 * When this flag is on, all SMS/MMS sent/received are stored by system automatically 1554 * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system 1555 * automatically 1556 * 1557 * This flag can only be changed by default SMS apps 1558 * 1559 * @param enabled Whether to enable message auto persisting 1560 * {@hide} 1561 */ 1562 public void setAutoPersisting(boolean enabled) { 1563 try { 1564 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1565 if (iMms != null) { 1566 iMms.setAutoPersisting(ActivityThread.currentPackageName(), enabled); 1567 } 1568 } catch (RemoteException ex) { 1569 // ignore it 1570 } 1571 } 1572 1573 /** 1574 * Get the value of the flag to automatically write sent/received SMS/MMS messages into system 1575 * 1576 * When this flag is on, all SMS/MMS sent/received are stored by system automatically 1577 * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system 1578 * automatically 1579 * 1580 * @return the current value of the auto persist flag 1581 * {@hide} 1582 */ 1583 public boolean getAutoPersisting() { 1584 try { 1585 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1586 if (iMms != null) { 1587 return iMms.getAutoPersisting(); 1588 } 1589 } catch (RemoteException ex) { 1590 // ignore it 1591 } 1592 return false; 1593 } 1594 1595 /** 1596 * Get carrier-dependent configuration values. 1597 * 1598 * @return bundle key/values pairs of configuration values 1599 */ 1600 public Bundle getCarrierConfigValues() { 1601 try { 1602 IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms")); 1603 if (iMms != null) { 1604 return iMms.getCarrierConfigValues(getSubscriptionId()); 1605 } 1606 } catch (RemoteException ex) { 1607 // ignore it 1608 } 1609 return null; 1610 } 1611 1612 /** 1613 * Create a single use app specific incoming SMS request for the the calling package. 1614 * 1615 * This method returns a token that if included in a subsequent incoming SMS message will cause 1616 * {@code intent} to be sent with the SMS data. 1617 * 1618 * The token is only good for one use, after an SMS has been received containing the token all 1619 * subsequent SMS messages with the token will be routed as normal. 1620 * 1621 * An app can only have one request at a time, if the app already has a request pending it will 1622 * be replaced with a new request. 1623 * 1624 * @return Token to include in an SMS message. The token will be 11 characters long. 1625 * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent 1626 */ 1627 public String createAppSpecificSmsToken(PendingIntent intent) { 1628 try { 1629 ISms iccSms = getISmsServiceOrThrow(); 1630 return iccSms.createAppSpecificSmsToken(getSubscriptionId(), 1631 ActivityThread.currentPackageName(), intent); 1632 1633 } catch (RemoteException ex) { 1634 ex.rethrowFromSystemServer(); 1635 return null; 1636 } 1637 } 1638 1639 /** 1640 * Filters a bundle to only contain MMS config variables. 1641 * 1642 * This is for use with bundles returned by {@link CarrierConfigManager} which contain MMS 1643 * config and unrelated config. It is assumed that all MMS_CONFIG_* keys are present in the 1644 * supplied bundle. 1645 * 1646 * @param config a Bundle that contains MMS config variables and possibly more. 1647 * @return a new Bundle that only contains the MMS_CONFIG_* keys defined above. 1648 * @hide 1649 */ 1650 public static Bundle getMmsConfig(BaseBundle config) { 1651 Bundle filtered = new Bundle(); 1652 filtered.putBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID, 1653 config.getBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID)); 1654 filtered.putBoolean(MMS_CONFIG_MMS_ENABLED, config.getBoolean(MMS_CONFIG_MMS_ENABLED)); 1655 filtered.putBoolean(MMS_CONFIG_GROUP_MMS_ENABLED, 1656 config.getBoolean(MMS_CONFIG_GROUP_MMS_ENABLED)); 1657 filtered.putBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED, 1658 config.getBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED)); 1659 filtered.putBoolean(MMS_CONFIG_ALIAS_ENABLED, config.getBoolean(MMS_CONFIG_ALIAS_ENABLED)); 1660 filtered.putBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO, 1661 config.getBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO)); 1662 filtered.putBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED, 1663 config.getBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED)); 1664 filtered.putBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED, 1665 config.getBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED)); 1666 filtered.putBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, 1667 config.getBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION)); 1668 filtered.putBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES, 1669 config.getBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES)); 1670 filtered.putBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED, 1671 config.getBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED)); 1672 filtered.putBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED, 1673 config.getBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED)); 1674 filtered.putBoolean(MMS_CONFIG_CLOSE_CONNECTION, 1675 config.getBoolean(MMS_CONFIG_CLOSE_CONNECTION)); 1676 filtered.putInt(MMS_CONFIG_MAX_MESSAGE_SIZE, config.getInt(MMS_CONFIG_MAX_MESSAGE_SIZE)); 1677 filtered.putInt(MMS_CONFIG_MAX_IMAGE_WIDTH, config.getInt(MMS_CONFIG_MAX_IMAGE_WIDTH)); 1678 filtered.putInt(MMS_CONFIG_MAX_IMAGE_HEIGHT, config.getInt(MMS_CONFIG_MAX_IMAGE_HEIGHT)); 1679 filtered.putInt(MMS_CONFIG_RECIPIENT_LIMIT, config.getInt(MMS_CONFIG_RECIPIENT_LIMIT)); 1680 filtered.putInt(MMS_CONFIG_ALIAS_MIN_CHARS, config.getInt(MMS_CONFIG_ALIAS_MIN_CHARS)); 1681 filtered.putInt(MMS_CONFIG_ALIAS_MAX_CHARS, config.getInt(MMS_CONFIG_ALIAS_MAX_CHARS)); 1682 filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD, 1683 config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD)); 1684 filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD, 1685 config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD)); 1686 filtered.putInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE, 1687 config.getInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE)); 1688 filtered.putInt(MMS_CONFIG_SUBJECT_MAX_LENGTH, 1689 config.getInt(MMS_CONFIG_SUBJECT_MAX_LENGTH)); 1690 filtered.putInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT, 1691 config.getInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT)); 1692 filtered.putString(MMS_CONFIG_UA_PROF_TAG_NAME, 1693 config.getString(MMS_CONFIG_UA_PROF_TAG_NAME)); 1694 filtered.putString(MMS_CONFIG_USER_AGENT, config.getString(MMS_CONFIG_USER_AGENT)); 1695 filtered.putString(MMS_CONFIG_UA_PROF_URL, config.getString(MMS_CONFIG_UA_PROF_URL)); 1696 filtered.putString(MMS_CONFIG_HTTP_PARAMS, config.getString(MMS_CONFIG_HTTP_PARAMS)); 1697 filtered.putString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER, 1698 config.getString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER)); 1699 filtered.putString(MMS_CONFIG_NAI_SUFFIX, config.getString(MMS_CONFIG_NAI_SUFFIX)); 1700 filtered.putBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS, 1701 config.getBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS)); 1702 filtered.putBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER, 1703 config.getBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER)); 1704 return filtered; 1705 } 1706 1707} 1708