GsmMmiCode.java revision d720945f2be5ea5fe0faf67e67d9ea0e184eba67
1/* 2 * Copyright (C) 2006 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 com.android.internal.telephony.gsm; 18 19import android.content.Context; 20import com.android.internal.telephony.*; 21import com.android.internal.telephony.uicc.IccRecords; 22import com.android.internal.telephony.uicc.UiccCardApplication; 23import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState; 24 25import android.os.*; 26import android.telephony.PhoneNumberUtils; 27import android.text.SpannableStringBuilder; 28import android.text.TextUtils; 29import android.telephony.Rlog; 30 31import static com.android.internal.telephony.CommandsInterface.*; 32 33import java.util.regex.Pattern; 34import java.util.regex.Matcher; 35 36/** 37 * The motto for this file is: 38 * 39 * "NOTE: By using the # as a separator, most cases are expected to be unambiguous." 40 * -- TS 22.030 6.5.2 41 * 42 * {@hide} 43 * 44 */ 45public final class GsmMmiCode extends Handler implements MmiCode { 46 static final String LOG_TAG = "GSM"; 47 48 //***** Constants 49 50 // Max Size of the Short Code (aka Short String from TS 22.030 6.5.2) 51 static final int MAX_LENGTH_SHORT_CODE = 2; 52 53 // TS 22.030 6.5.2 Every Short String USSD command will end with #-key 54 // (known as #-String) 55 static final char END_OF_USSD_COMMAND = '#'; 56 57 // From TS 22.030 6.5.2 58 static final String ACTION_ACTIVATE = "*"; 59 static final String ACTION_DEACTIVATE = "#"; 60 static final String ACTION_INTERROGATE = "*#"; 61 static final String ACTION_REGISTER = "**"; 62 static final String ACTION_ERASURE = "##"; 63 64 // Supp Service codes from TS 22.030 Annex B 65 66 //Called line presentation 67 static final String SC_CLIP = "30"; 68 static final String SC_CLIR = "31"; 69 70 // Call Forwarding 71 static final String SC_CFU = "21"; 72 static final String SC_CFB = "67"; 73 static final String SC_CFNRy = "61"; 74 static final String SC_CFNR = "62"; 75 76 static final String SC_CF_All = "002"; 77 static final String SC_CF_All_Conditional = "004"; 78 79 // Call Waiting 80 static final String SC_WAIT = "43"; 81 82 // Call Barring 83 static final String SC_BAOC = "33"; 84 static final String SC_BAOIC = "331"; 85 static final String SC_BAOICxH = "332"; 86 static final String SC_BAIC = "35"; 87 static final String SC_BAICr = "351"; 88 89 static final String SC_BA_ALL = "330"; 90 static final String SC_BA_MO = "333"; 91 static final String SC_BA_MT = "353"; 92 93 // Supp Service Password registration 94 static final String SC_PWD = "03"; 95 96 // PIN/PIN2/PUK/PUK2 97 static final String SC_PIN = "04"; 98 static final String SC_PIN2 = "042"; 99 static final String SC_PUK = "05"; 100 static final String SC_PUK2 = "052"; 101 102 //***** Event Constants 103 104 static final int EVENT_SET_COMPLETE = 1; 105 static final int EVENT_GET_CLIR_COMPLETE = 2; 106 static final int EVENT_QUERY_CF_COMPLETE = 3; 107 static final int EVENT_USSD_COMPLETE = 4; 108 static final int EVENT_QUERY_COMPLETE = 5; 109 static final int EVENT_SET_CFF_COMPLETE = 6; 110 static final int EVENT_USSD_CANCEL_COMPLETE = 7; 111 112 //***** Instance Variables 113 114 GSMPhone phone; 115 Context context; 116 UiccCardApplication mUiccApplication; 117 IccRecords mIccRecords; 118 119 String action; // One of ACTION_* 120 String sc; // Service Code 121 String sia, sib, sic; // Service Info a,b,c 122 String poundString; // Entire MMI string up to and including # 123 String dialingNumber; 124 String pwd; // For password registration 125 126 /** Set to true in processCode, not at newFromDialString time */ 127 private boolean isPendingUSSD; 128 129 private boolean isUssdRequest; 130 131 State state = State.PENDING; 132 CharSequence message; 133 134 //***** Class Variables 135 136 137 // See TS 22.030 6.5.2 "Structure of the MMI" 138 139 static Pattern sPatternSuppService = Pattern.compile( 140 "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)"); 141/* 1 2 3 4 5 6 7 8 9 10 11 12 142 143 1 = Full string up to and including # 144 2 = action (activation/interrogation/registration/erasure) 145 3 = service code 146 5 = SIA 147 7 = SIB 148 9 = SIC 149 10 = dialing number 150*/ 151 152 static final int MATCH_GROUP_POUND_STRING = 1; 153 154 static final int MATCH_GROUP_ACTION = 2; 155 //(activation/interrogation/registration/erasure) 156 157 static final int MATCH_GROUP_SERVICE_CODE = 3; 158 static final int MATCH_GROUP_SIA = 5; 159 static final int MATCH_GROUP_SIB = 7; 160 static final int MATCH_GROUP_SIC = 9; 161 static final int MATCH_GROUP_PWD_CONFIRM = 11; 162 static final int MATCH_GROUP_DIALING_NUMBER = 12; 163 static private String[] sTwoDigitNumberPattern; 164 165 //***** Public Class methods 166 167 /** 168 * Some dial strings in GSM are defined to do non-call setup 169 * things, such as modify or query supplementary service settings (eg, call 170 * forwarding). These are generally referred to as "MMI codes". 171 * We look to see if the dial string contains a valid MMI code (potentially 172 * with a dial string at the end as well) and return info here. 173 * 174 * If the dial string contains no MMI code, we return an instance with 175 * only "dialingNumber" set 176 * 177 * Please see flow chart in TS 22.030 6.5.3.2 178 */ 179 180 static GsmMmiCode 181 newFromDialString(String dialString, GSMPhone phone, UiccCardApplication app) { 182 Matcher m; 183 GsmMmiCode ret = null; 184 185 m = sPatternSuppService.matcher(dialString); 186 187 // Is this formatted like a standard supplementary service code? 188 if (m.matches()) { 189 ret = new GsmMmiCode(phone, app); 190 ret.poundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING)); 191 ret.action = makeEmptyNull(m.group(MATCH_GROUP_ACTION)); 192 ret.sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE)); 193 ret.sia = makeEmptyNull(m.group(MATCH_GROUP_SIA)); 194 ret.sib = makeEmptyNull(m.group(MATCH_GROUP_SIB)); 195 ret.sic = makeEmptyNull(m.group(MATCH_GROUP_SIC)); 196 ret.pwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM)); 197 ret.dialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER)); 198 // According to TS 22.030 6.5.2 "Structure of the MMI", 199 // the dialing number should not ending with #. 200 // The dialing number ending # is treated as unique USSD, 201 // eg, *400#16 digit number# to recharge the prepaid card 202 // in India operator(Mumbai MTNL) 203 if(ret.dialingNumber != null && 204 ret.dialingNumber.endsWith("#") && 205 dialString.endsWith("#")){ 206 ret = new GsmMmiCode(phone, app); 207 ret.poundString = dialString; 208 } 209 } else if (dialString.endsWith("#")) { 210 // TS 22.030 sec 6.5.3.2 211 // "Entry of any characters defined in the 3GPP TS 23.038 [8] Default Alphabet 212 // (up to the maximum defined in 3GPP TS 24.080 [10]), followed by #SEND". 213 214 ret = new GsmMmiCode(phone, app); 215 ret.poundString = dialString; 216 } else if (isTwoDigitShortCode(phone.getContext(), dialString)) { 217 //Is a country-specific exception to short codes as defined in TS 22.030, 6.5.3.2 218 ret = null; 219 } else if (isShortCode(dialString, phone)) { 220 // this may be a short code, as defined in TS 22.030, 6.5.3.2 221 ret = new GsmMmiCode(phone, app); 222 ret.dialingNumber = dialString; 223 } 224 225 return ret; 226 } 227 228 static GsmMmiCode 229 newNetworkInitiatedUssd (String ussdMessage, 230 boolean isUssdRequest, GSMPhone phone, UiccCardApplication app) { 231 GsmMmiCode ret; 232 233 ret = new GsmMmiCode(phone, app); 234 235 ret.message = ussdMessage; 236 ret.isUssdRequest = isUssdRequest; 237 238 // If it's a request, set to PENDING so that it's cancelable. 239 if (isUssdRequest) { 240 ret.isPendingUSSD = true; 241 ret.state = State.PENDING; 242 } else { 243 ret.state = State.COMPLETE; 244 } 245 246 return ret; 247 } 248 249 static GsmMmiCode newFromUssdUserInput(String ussdMessge, 250 GSMPhone phone, 251 UiccCardApplication app) { 252 GsmMmiCode ret = new GsmMmiCode(phone, app); 253 254 ret.message = ussdMessge; 255 ret.state = State.PENDING; 256 ret.isPendingUSSD = true; 257 258 return ret; 259 } 260 261 //***** Private Class methods 262 263 /** make empty strings be null. 264 * Regexp returns empty strings for empty groups 265 */ 266 private static String 267 makeEmptyNull (String s) { 268 if (s != null && s.length() == 0) return null; 269 270 return s; 271 } 272 273 /** returns true of the string is empty or null */ 274 private static boolean 275 isEmptyOrNull(CharSequence s) { 276 return s == null || (s.length() == 0); 277 } 278 279 280 private static int 281 scToCallForwardReason(String sc) { 282 if (sc == null) { 283 throw new RuntimeException ("invalid call forward sc"); 284 } 285 286 if (sc.equals(SC_CF_All)) { 287 return CommandsInterface.CF_REASON_ALL; 288 } else if (sc.equals(SC_CFU)) { 289 return CommandsInterface.CF_REASON_UNCONDITIONAL; 290 } else if (sc.equals(SC_CFB)) { 291 return CommandsInterface.CF_REASON_BUSY; 292 } else if (sc.equals(SC_CFNR)) { 293 return CommandsInterface.CF_REASON_NOT_REACHABLE; 294 } else if (sc.equals(SC_CFNRy)) { 295 return CommandsInterface.CF_REASON_NO_REPLY; 296 } else if (sc.equals(SC_CF_All_Conditional)) { 297 return CommandsInterface.CF_REASON_ALL_CONDITIONAL; 298 } else { 299 throw new RuntimeException ("invalid call forward sc"); 300 } 301 } 302 303 private static int 304 siToServiceClass(String si) { 305 if (si == null || si.length() == 0) { 306 return SERVICE_CLASS_NONE; 307 } else { 308 // NumberFormatException should cause MMI fail 309 int serviceCode = Integer.parseInt(si, 10); 310 311 switch (serviceCode) { 312 case 10: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX + SERVICE_CLASS_VOICE; 313 case 11: return SERVICE_CLASS_VOICE; 314 case 12: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX; 315 case 13: return SERVICE_CLASS_FAX; 316 317 case 16: return SERVICE_CLASS_SMS; 318 319 case 19: return SERVICE_CLASS_FAX + SERVICE_CLASS_VOICE; 320/* 321 Note for code 20: 322 From TS 22.030 Annex C: 323 "All GPRS bearer services" are not included in "All tele and bearer services" 324 and "All bearer services"." 325....so SERVICE_CLASS_DATA, which (according to 27.007) includes GPRS 326*/ 327 case 20: return SERVICE_CLASS_DATA_ASYNC + SERVICE_CLASS_DATA_SYNC; 328 329 case 21: return SERVICE_CLASS_PAD + SERVICE_CLASS_DATA_ASYNC; 330 case 22: return SERVICE_CLASS_PACKET + SERVICE_CLASS_DATA_SYNC; 331 case 24: return SERVICE_CLASS_DATA_SYNC; 332 case 25: return SERVICE_CLASS_DATA_ASYNC; 333 case 26: return SERVICE_CLASS_DATA_SYNC + SERVICE_CLASS_VOICE; 334 case 99: return SERVICE_CLASS_PACKET; 335 336 default: 337 throw new RuntimeException("unsupported MMI service code " + si); 338 } 339 } 340 } 341 342 private static int 343 siToTime (String si) { 344 if (si == null || si.length() == 0) { 345 return 0; 346 } else { 347 // NumberFormatException should cause MMI fail 348 return Integer.parseInt(si, 10); 349 } 350 } 351 352 static boolean 353 isServiceCodeCallForwarding(String sc) { 354 return sc != null && 355 (sc.equals(SC_CFU) 356 || sc.equals(SC_CFB) || sc.equals(SC_CFNRy) 357 || sc.equals(SC_CFNR) || sc.equals(SC_CF_All) 358 || sc.equals(SC_CF_All_Conditional)); 359 } 360 361 static boolean 362 isServiceCodeCallBarring(String sc) { 363 return sc != null && 364 (sc.equals(SC_BAOC) 365 || sc.equals(SC_BAOIC) 366 || sc.equals(SC_BAOICxH) 367 || sc.equals(SC_BAIC) 368 || sc.equals(SC_BAICr) 369 || sc.equals(SC_BA_ALL) 370 || sc.equals(SC_BA_MO) 371 || sc.equals(SC_BA_MT)); 372 } 373 374 static String 375 scToBarringFacility(String sc) { 376 if (sc == null) { 377 throw new RuntimeException ("invalid call barring sc"); 378 } 379 380 if (sc.equals(SC_BAOC)) { 381 return CommandsInterface.CB_FACILITY_BAOC; 382 } else if (sc.equals(SC_BAOIC)) { 383 return CommandsInterface.CB_FACILITY_BAOIC; 384 } else if (sc.equals(SC_BAOICxH)) { 385 return CommandsInterface.CB_FACILITY_BAOICxH; 386 } else if (sc.equals(SC_BAIC)) { 387 return CommandsInterface.CB_FACILITY_BAIC; 388 } else if (sc.equals(SC_BAICr)) { 389 return CommandsInterface.CB_FACILITY_BAICr; 390 } else if (sc.equals(SC_BA_ALL)) { 391 return CommandsInterface.CB_FACILITY_BA_ALL; 392 } else if (sc.equals(SC_BA_MO)) { 393 return CommandsInterface.CB_FACILITY_BA_MO; 394 } else if (sc.equals(SC_BA_MT)) { 395 return CommandsInterface.CB_FACILITY_BA_MT; 396 } else { 397 throw new RuntimeException ("invalid call barring sc"); 398 } 399 } 400 401 //***** Constructor 402 403 GsmMmiCode (GSMPhone phone, UiccCardApplication app) { 404 // The telephony unit-test cases may create GsmMmiCode's 405 // in secondary threads 406 super(phone.getHandler().getLooper()); 407 this.phone = phone; 408 this.context = phone.getContext(); 409 mUiccApplication = app; 410 if (app != null) { 411 mIccRecords = app.getIccRecords(); 412 } 413 } 414 415 //***** MmiCode implementation 416 417 public State 418 getState() { 419 return state; 420 } 421 422 public CharSequence 423 getMessage() { 424 return message; 425 } 426 427 // inherited javadoc suffices 428 public void 429 cancel() { 430 // Complete or failed cannot be cancelled 431 if (state == State.COMPLETE || state == State.FAILED) { 432 return; 433 } 434 435 state = State.CANCELLED; 436 437 if (isPendingUSSD) { 438 /* 439 * There can only be one pending USSD session, so tell the radio to 440 * cancel it. 441 */ 442 phone.mCM.cancelPendingUssd(obtainMessage(EVENT_USSD_CANCEL_COMPLETE, this)); 443 444 /* 445 * Don't call phone.onMMIDone here; wait for CANCEL_COMPLETE notice 446 * from RIL. 447 */ 448 } else { 449 // TODO in cases other than USSD, it would be nice to cancel 450 // the pending radio operation. This requires RIL cancellation 451 // support, which does not presently exist. 452 453 phone.onMMIDone (this); 454 } 455 456 } 457 458 public boolean isCancelable() { 459 /* Can only cancel pending USSD sessions. */ 460 return isPendingUSSD; 461 } 462 463 //***** Instance Methods 464 465 /** Does this dial string contain a structured or unstructured MMI code? */ 466 boolean 467 isMMI() { 468 return poundString != null; 469 } 470 471 /* Is this a 1 or 2 digit "short code" as defined in TS 22.030 sec 6.5.3.2? */ 472 boolean 473 isShortCode() { 474 return poundString == null 475 && dialingNumber != null && dialingNumber.length() <= 2; 476 477 } 478 479 static private boolean 480 isTwoDigitShortCode(Context context, String dialString) { 481 Rlog.d(LOG_TAG, "isTwoDigitShortCode"); 482 483 if (dialString == null || dialString.length() != 2) return false; 484 485 if (sTwoDigitNumberPattern == null) { 486 sTwoDigitNumberPattern = context.getResources().getStringArray( 487 com.android.internal.R.array.config_twoDigitNumberPattern); 488 } 489 490 for (String dialnumber : sTwoDigitNumberPattern) { 491 Rlog.d(LOG_TAG, "Two Digit Number Pattern " + dialnumber); 492 if (dialString.equals(dialnumber)) { 493 Rlog.d(LOG_TAG, "Two Digit Number Pattern -true"); 494 return true; 495 } 496 } 497 Rlog.d(LOG_TAG, "Two Digit Number Pattern -false"); 498 return false; 499 } 500 501 /** 502 * Helper function for newFromDialString. Returns true if dialString appears 503 * to be a short code AND conditions are correct for it to be treated as 504 * such. 505 */ 506 static private boolean isShortCode(String dialString, GSMPhone phone) { 507 // Refer to TS 22.030 Figure 3.5.3.2: 508 if (dialString == null) { 509 return false; 510 } 511 512 // Illegal dial string characters will give a ZERO length. 513 // At this point we do not want to crash as any application with 514 // call privileges may send a non dial string. 515 // It return false as when the dialString is equal to NULL. 516 if (dialString.length() == 0) { 517 return false; 518 } 519 520 if (PhoneNumberUtils.isLocalEmergencyNumber(dialString, phone.getContext())) { 521 return false; 522 } else { 523 return isShortCodeUSSD(dialString, phone); 524 } 525 } 526 527 /** 528 * Helper function for isShortCode. Returns true if dialString appears to be 529 * a short code and it is a USSD structure 530 * 531 * According to the 3PGG TS 22.030 specification Figure 3.5.3.2: A 1 or 2 532 * digit "short code" is treated as USSD if it is entered while on a call or 533 * does not satisfy the condition (exactly 2 digits && starts with '1'), there 534 * are however exceptions to this rule (see below) 535 * 536 * Exception (1) to Call initiation is: If the user of the device is already in a call 537 * and enters a Short String without any #-key at the end and the length of the Short String is 538 * equal or less then the MAX_LENGTH_SHORT_CODE [constant that is equal to 2] 539 * 540 * The phone shall initiate a USSD/SS commands. 541 * 542 * Exception (2) to Call initiation is: If the user of the device enters one 543 * Digit followed by the #-key. This rule defines this String as the 544 * #-String which is a USSD/SS command. 545 * 546 * The phone shall initiate a USSD/SS command. 547 */ 548 static private boolean isShortCodeUSSD(String dialString, GSMPhone phone) { 549 if (dialString != null) { 550 if (phone.isInCall()) { 551 // The maximum length of a Short Code (aka Short String) is 2 552 if (dialString.length() <= MAX_LENGTH_SHORT_CODE) { 553 return true; 554 } 555 } 556 557 // The maximum length of a Short Code (aka Short String) is 2 558 if (dialString.length() <= MAX_LENGTH_SHORT_CODE) { 559 if (dialString.charAt(dialString.length() - 1) == END_OF_USSD_COMMAND) { 560 return true; 561 } 562 } 563 } 564 return false; 565 } 566 567 /** 568 * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related 569 */ 570 boolean isPinCommand() { 571 return sc != null && (sc.equals(SC_PIN) || sc.equals(SC_PIN2) 572 || sc.equals(SC_PUK) || sc.equals(SC_PUK2)); 573 } 574 575 /** 576 * See TS 22.030 Annex B. 577 * In temporary mode, to suppress CLIR for a single call, enter: 578 * " * 31 # [called number] SEND " 579 * In temporary mode, to invoke CLIR for a single call enter: 580 * " # 31 # [called number] SEND " 581 */ 582 boolean 583 isTemporaryModeCLIR() { 584 return sc != null && sc.equals(SC_CLIR) && dialingNumber != null 585 && (isActivate() || isDeactivate()); 586 } 587 588 /** 589 * returns CommandsInterface.CLIR_* 590 * See also isTemporaryModeCLIR() 591 */ 592 int 593 getCLIRMode() { 594 if (sc != null && sc.equals(SC_CLIR)) { 595 if (isActivate()) { 596 return CommandsInterface.CLIR_SUPPRESSION; 597 } else if (isDeactivate()) { 598 return CommandsInterface.CLIR_INVOCATION; 599 } 600 } 601 602 return CommandsInterface.CLIR_DEFAULT; 603 } 604 605 boolean isActivate() { 606 return action != null && action.equals(ACTION_ACTIVATE); 607 } 608 609 boolean isDeactivate() { 610 return action != null && action.equals(ACTION_DEACTIVATE); 611 } 612 613 boolean isInterrogate() { 614 return action != null && action.equals(ACTION_INTERROGATE); 615 } 616 617 boolean isRegister() { 618 return action != null && action.equals(ACTION_REGISTER); 619 } 620 621 boolean isErasure() { 622 return action != null && action.equals(ACTION_ERASURE); 623 } 624 625 /** 626 * Returns true if this is a USSD code that's been submitted to the 627 * network...eg, after processCode() is called 628 */ 629 public boolean isPendingUSSD() { 630 return isPendingUSSD; 631 } 632 633 public boolean isUssdRequest() { 634 return isUssdRequest; 635 } 636 637 /** Process a MMI code or short code...anything that isn't a dialing number */ 638 void 639 processCode () { 640 try { 641 if (isShortCode()) { 642 Rlog.d(LOG_TAG, "isShortCode"); 643 // These just get treated as USSD. 644 sendUssd(dialingNumber); 645 } else if (dialingNumber != null) { 646 // We should have no dialing numbers here 647 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 648 } else if (sc != null && sc.equals(SC_CLIP)) { 649 Rlog.d(LOG_TAG, "is CLIP"); 650 if (isInterrogate()) { 651 phone.mCM.queryCLIP( 652 obtainMessage(EVENT_QUERY_COMPLETE, this)); 653 } else { 654 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 655 } 656 } else if (sc != null && sc.equals(SC_CLIR)) { 657 Rlog.d(LOG_TAG, "is CLIR"); 658 if (isActivate()) { 659 phone.mCM.setCLIR(CommandsInterface.CLIR_INVOCATION, 660 obtainMessage(EVENT_SET_COMPLETE, this)); 661 } else if (isDeactivate()) { 662 phone.mCM.setCLIR(CommandsInterface.CLIR_SUPPRESSION, 663 obtainMessage(EVENT_SET_COMPLETE, this)); 664 } else if (isInterrogate()) { 665 phone.mCM.getCLIR( 666 obtainMessage(EVENT_GET_CLIR_COMPLETE, this)); 667 } else { 668 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 669 } 670 } else if (isServiceCodeCallForwarding(sc)) { 671 Rlog.d(LOG_TAG, "is CF"); 672 673 String dialingNumber = sia; 674 int serviceClass = siToServiceClass(sib); 675 int reason = scToCallForwardReason(sc); 676 int time = siToTime(sic); 677 678 if (isInterrogate()) { 679 phone.mCM.queryCallForwardStatus( 680 reason, serviceClass, dialingNumber, 681 obtainMessage(EVENT_QUERY_CF_COMPLETE, this)); 682 } else { 683 int cfAction; 684 685 if (isActivate()) { 686 cfAction = CommandsInterface.CF_ACTION_ENABLE; 687 } else if (isDeactivate()) { 688 cfAction = CommandsInterface.CF_ACTION_DISABLE; 689 } else if (isRegister()) { 690 cfAction = CommandsInterface.CF_ACTION_REGISTRATION; 691 } else if (isErasure()) { 692 cfAction = CommandsInterface.CF_ACTION_ERASURE; 693 } else { 694 throw new RuntimeException ("invalid action"); 695 } 696 697 int isSettingUnconditionalVoice = 698 (((reason == CommandsInterface.CF_REASON_UNCONDITIONAL) || 699 (reason == CommandsInterface.CF_REASON_ALL)) && 700 (((serviceClass & CommandsInterface.SERVICE_CLASS_VOICE) != 0) || 701 (serviceClass == CommandsInterface.SERVICE_CLASS_NONE))) ? 1 : 0; 702 703 int isEnableDesired = 704 ((cfAction == CommandsInterface.CF_ACTION_ENABLE) || 705 (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0; 706 707 Rlog.d(LOG_TAG, "is CF setCallForward"); 708 phone.mCM.setCallForward(cfAction, reason, serviceClass, 709 dialingNumber, time, obtainMessage( 710 EVENT_SET_CFF_COMPLETE, 711 isSettingUnconditionalVoice, 712 isEnableDesired, this)); 713 } 714 } else if (isServiceCodeCallBarring(sc)) { 715 // sia = password 716 // sib = basic service group 717 718 String password = sia; 719 int serviceClass = siToServiceClass(sib); 720 String facility = scToBarringFacility(sc); 721 722 if (isInterrogate()) { 723 phone.mCM.queryFacilityLock(facility, password, 724 serviceClass, obtainMessage(EVENT_QUERY_COMPLETE, this)); 725 } else if (isActivate() || isDeactivate()) { 726 phone.mCM.setFacilityLock(facility, isActivate(), password, 727 serviceClass, obtainMessage(EVENT_SET_COMPLETE, this)); 728 } else { 729 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 730 } 731 732 } else if (sc != null && sc.equals(SC_PWD)) { 733 // sia = fac 734 // sib = old pwd 735 // sic = new pwd 736 // pwd = new pwd 737 String facility; 738 String oldPwd = sib; 739 String newPwd = sic; 740 if (isActivate() || isRegister()) { 741 // Even though ACTIVATE is acceptable, this is really termed a REGISTER 742 action = ACTION_REGISTER; 743 744 if (sia == null) { 745 // If sc was not specified, treat it as BA_ALL. 746 facility = CommandsInterface.CB_FACILITY_BA_ALL; 747 } else { 748 facility = scToBarringFacility(sia); 749 } 750 if (newPwd.equals(pwd)) { 751 phone.mCM.changeBarringPassword(facility, oldPwd, 752 newPwd, obtainMessage(EVENT_SET_COMPLETE, this)); 753 } else { 754 // password mismatch; return error 755 handlePasswordError(com.android.internal.R.string.passwordIncorrect); 756 } 757 } else { 758 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 759 } 760 761 } else if (sc != null && sc.equals(SC_WAIT)) { 762 // sia = basic service group 763 int serviceClass = siToServiceClass(sia); 764 765 if (isActivate() || isDeactivate()) { 766 phone.mCM.setCallWaiting(isActivate(), serviceClass, 767 obtainMessage(EVENT_SET_COMPLETE, this)); 768 } else if (isInterrogate()) { 769 phone.mCM.queryCallWaiting(serviceClass, 770 obtainMessage(EVENT_QUERY_COMPLETE, this)); 771 } else { 772 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 773 } 774 } else if (isPinCommand()) { 775 // sia = old PIN or PUK 776 // sib = new PIN 777 // sic = new PIN 778 String oldPinOrPuk = sia; 779 String newPin = sib; 780 int pinLen = newPin.length(); 781 if (isRegister()) { 782 if (!newPin.equals(sic)) { 783 // password mismatch; return error 784 handlePasswordError(com.android.internal.R.string.mismatchPin); 785 } else if (pinLen < 4 || pinLen > 8 ) { 786 // invalid length 787 handlePasswordError(com.android.internal.R.string.invalidPin); 788 } else if (sc.equals(SC_PIN) && 789 mUiccApplication != null && 790 mUiccApplication.getState() == AppState.APPSTATE_PUK ) { 791 // Sim is puk-locked 792 handlePasswordError(com.android.internal.R.string.needPuk); 793 } else { 794 // pre-checks OK 795 if (sc.equals(SC_PIN)) { 796 phone.mCM.changeIccPin(oldPinOrPuk, newPin, 797 obtainMessage(EVENT_SET_COMPLETE, this)); 798 } else if (sc.equals(SC_PIN2)) { 799 phone.mCM.changeIccPin2(oldPinOrPuk, newPin, 800 obtainMessage(EVENT_SET_COMPLETE, this)); 801 } else if (sc.equals(SC_PUK)) { 802 phone.mCM.supplyIccPuk(oldPinOrPuk, newPin, 803 obtainMessage(EVENT_SET_COMPLETE, this)); 804 } else if (sc.equals(SC_PUK2)) { 805 phone.mCM.supplyIccPuk2(oldPinOrPuk, newPin, 806 obtainMessage(EVENT_SET_COMPLETE, this)); 807 } 808 } 809 } else { 810 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 811 } 812 } else if (poundString != null) { 813 sendUssd(poundString); 814 } else { 815 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 816 } 817 } catch (RuntimeException exc) { 818 state = State.FAILED; 819 message = context.getText(com.android.internal.R.string.mmiError); 820 phone.onMMIDone(this); 821 } 822 } 823 824 private void handlePasswordError(int res) { 825 state = State.FAILED; 826 StringBuilder sb = new StringBuilder(getScString()); 827 sb.append("\n"); 828 sb.append(context.getText(res)); 829 message = sb; 830 phone.onMMIDone(this); 831 } 832 833 /** 834 * Called from GSMPhone 835 * 836 * An unsolicited USSD NOTIFY or REQUEST has come in matching 837 * up with this pending USSD request 838 * 839 * Note: If REQUEST, this exchange is complete, but the session remains 840 * active (ie, the network expects user input). 841 */ 842 void 843 onUssdFinished(String ussdMessage, boolean isUssdRequest) { 844 if (state == State.PENDING) { 845 if (ussdMessage == null) { 846 message = context.getText(com.android.internal.R.string.mmiComplete); 847 } else { 848 message = ussdMessage; 849 } 850 this.isUssdRequest = isUssdRequest; 851 // If it's a request, leave it PENDING so that it's cancelable. 852 if (!isUssdRequest) { 853 state = State.COMPLETE; 854 } 855 856 phone.onMMIDone(this); 857 } 858 } 859 860 /** 861 * Called from GSMPhone 862 * 863 * The radio has reset, and this is still pending 864 */ 865 866 void 867 onUssdFinishedError() { 868 if (state == State.PENDING) { 869 state = State.FAILED; 870 message = context.getText(com.android.internal.R.string.mmiError); 871 872 phone.onMMIDone(this); 873 } 874 } 875 876 void sendUssd(String ussdMessage) { 877 // Treat this as a USSD string 878 isPendingUSSD = true; 879 880 // Note that unlike most everything else, the USSD complete 881 // response does not complete this MMI code...we wait for 882 // an unsolicited USSD "Notify" or "Request". 883 // The matching up of this is done in GSMPhone. 884 885 phone.mCM.sendUSSD(ussdMessage, 886 obtainMessage(EVENT_USSD_COMPLETE, this)); 887 } 888 889 /** Called from GSMPhone.handleMessage; not a Handler subclass */ 890 public void 891 handleMessage (Message msg) { 892 AsyncResult ar; 893 894 switch (msg.what) { 895 case EVENT_SET_COMPLETE: 896 ar = (AsyncResult) (msg.obj); 897 898 onSetComplete(ar); 899 break; 900 901 case EVENT_SET_CFF_COMPLETE: 902 ar = (AsyncResult) (msg.obj); 903 904 /* 905 * msg.arg1 = 1 means to set unconditional voice call forwarding 906 * msg.arg2 = 1 means to enable voice call forwarding 907 */ 908 if ((ar.exception == null) && (msg.arg1 == 1)) { 909 boolean cffEnabled = (msg.arg2 == 1); 910 if (mIccRecords != null) { 911 mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled); 912 } 913 } 914 915 onSetComplete(ar); 916 break; 917 918 case EVENT_GET_CLIR_COMPLETE: 919 ar = (AsyncResult) (msg.obj); 920 onGetClirComplete(ar); 921 break; 922 923 case EVENT_QUERY_CF_COMPLETE: 924 ar = (AsyncResult) (msg.obj); 925 onQueryCfComplete(ar); 926 break; 927 928 case EVENT_QUERY_COMPLETE: 929 ar = (AsyncResult) (msg.obj); 930 onQueryComplete(ar); 931 break; 932 933 case EVENT_USSD_COMPLETE: 934 ar = (AsyncResult) (msg.obj); 935 936 if (ar.exception != null) { 937 state = State.FAILED; 938 message = getErrorMessage(ar); 939 940 phone.onMMIDone(this); 941 } 942 943 // Note that unlike most everything else, the USSD complete 944 // response does not complete this MMI code...we wait for 945 // an unsolicited USSD "Notify" or "Request". 946 // The matching up of this is done in GSMPhone. 947 948 break; 949 950 case EVENT_USSD_CANCEL_COMPLETE: 951 phone.onMMIDone(this); 952 break; 953 } 954 } 955 //***** Private instance methods 956 957 private CharSequence getErrorMessage(AsyncResult ar) { 958 959 if (ar.exception instanceof CommandException) { 960 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); 961 if (err == CommandException.Error.FDN_CHECK_FAILURE) { 962 Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE"); 963 return context.getText(com.android.internal.R.string.mmiFdnError); 964 } 965 } 966 967 return context.getText(com.android.internal.R.string.mmiError); 968 } 969 970 private CharSequence getScString() { 971 if (sc != null) { 972 if (isServiceCodeCallBarring(sc)) { 973 return context.getText(com.android.internal.R.string.BaMmi); 974 } else if (isServiceCodeCallForwarding(sc)) { 975 return context.getText(com.android.internal.R.string.CfMmi); 976 } else if (sc.equals(SC_CLIP)) { 977 return context.getText(com.android.internal.R.string.ClipMmi); 978 } else if (sc.equals(SC_CLIR)) { 979 return context.getText(com.android.internal.R.string.ClirMmi); 980 } else if (sc.equals(SC_PWD)) { 981 return context.getText(com.android.internal.R.string.PwdMmi); 982 } else if (sc.equals(SC_WAIT)) { 983 return context.getText(com.android.internal.R.string.CwMmi); 984 } else if (isPinCommand()) { 985 return context.getText(com.android.internal.R.string.PinMmi); 986 } 987 } 988 989 return ""; 990 } 991 992 private void 993 onSetComplete(AsyncResult ar){ 994 StringBuilder sb = new StringBuilder(getScString()); 995 sb.append("\n"); 996 997 if (ar.exception != null) { 998 state = State.FAILED; 999 if (ar.exception instanceof CommandException) { 1000 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); 1001 if (err == CommandException.Error.PASSWORD_INCORRECT) { 1002 if (isPinCommand()) { 1003 // look specifically for the PUK commands and adjust 1004 // the message accordingly. 1005 if (sc.equals(SC_PUK) || sc.equals(SC_PUK2)) { 1006 sb.append(context.getText( 1007 com.android.internal.R.string.badPuk)); 1008 } else { 1009 sb.append(context.getText( 1010 com.android.internal.R.string.badPin)); 1011 } 1012 } else { 1013 sb.append(context.getText( 1014 com.android.internal.R.string.passwordIncorrect)); 1015 } 1016 } else if (err == CommandException.Error.SIM_PUK2) { 1017 sb.append(context.getText( 1018 com.android.internal.R.string.badPin)); 1019 sb.append("\n"); 1020 sb.append(context.getText( 1021 com.android.internal.R.string.needPuk2)); 1022 } else if (err == CommandException.Error.FDN_CHECK_FAILURE) { 1023 Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE"); 1024 sb.append(context.getText(com.android.internal.R.string.mmiFdnError)); 1025 } else { 1026 sb.append(context.getText( 1027 com.android.internal.R.string.mmiError)); 1028 } 1029 } else { 1030 sb.append(context.getText( 1031 com.android.internal.R.string.mmiError)); 1032 } 1033 } else if (isActivate()) { 1034 state = State.COMPLETE; 1035 sb.append(context.getText( 1036 com.android.internal.R.string.serviceEnabled)); 1037 // Record CLIR setting 1038 if (sc.equals(SC_CLIR)) { 1039 phone.saveClirSetting(CommandsInterface.CLIR_INVOCATION); 1040 } 1041 } else if (isDeactivate()) { 1042 state = State.COMPLETE; 1043 sb.append(context.getText( 1044 com.android.internal.R.string.serviceDisabled)); 1045 // Record CLIR setting 1046 if (sc.equals(SC_CLIR)) { 1047 phone.saveClirSetting(CommandsInterface.CLIR_SUPPRESSION); 1048 } 1049 } else if (isRegister()) { 1050 state = State.COMPLETE; 1051 sb.append(context.getText( 1052 com.android.internal.R.string.serviceRegistered)); 1053 } else if (isErasure()) { 1054 state = State.COMPLETE; 1055 sb.append(context.getText( 1056 com.android.internal.R.string.serviceErased)); 1057 } else { 1058 state = State.FAILED; 1059 sb.append(context.getText( 1060 com.android.internal.R.string.mmiError)); 1061 } 1062 1063 message = sb; 1064 phone.onMMIDone(this); 1065 } 1066 1067 private void 1068 onGetClirComplete(AsyncResult ar) { 1069 StringBuilder sb = new StringBuilder(getScString()); 1070 sb.append("\n"); 1071 1072 if (ar.exception != null) { 1073 state = State.FAILED; 1074 sb.append(getErrorMessage(ar)); 1075 } else { 1076 int clirArgs[]; 1077 1078 clirArgs = (int[])ar.result; 1079 1080 // the 'm' parameter from TS 27.007 7.7 1081 switch (clirArgs[1]) { 1082 case 0: // CLIR not provisioned 1083 sb.append(context.getText( 1084 com.android.internal.R.string.serviceNotProvisioned)); 1085 state = State.COMPLETE; 1086 break; 1087 1088 case 1: // CLIR provisioned in permanent mode 1089 sb.append(context.getText( 1090 com.android.internal.R.string.CLIRPermanent)); 1091 state = State.COMPLETE; 1092 break; 1093 1094 case 2: // unknown (e.g. no network, etc.) 1095 sb.append(context.getText( 1096 com.android.internal.R.string.mmiError)); 1097 state = State.FAILED; 1098 break; 1099 1100 case 3: // CLIR temporary mode presentation restricted 1101 1102 // the 'n' parameter from TS 27.007 7.7 1103 switch (clirArgs[0]) { 1104 default: 1105 case 0: // Default 1106 sb.append(context.getText( 1107 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1108 break; 1109 case 1: // CLIR invocation 1110 sb.append(context.getText( 1111 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1112 break; 1113 case 2: // CLIR suppression 1114 sb.append(context.getText( 1115 com.android.internal.R.string.CLIRDefaultOnNextCallOff)); 1116 break; 1117 } 1118 state = State.COMPLETE; 1119 break; 1120 1121 case 4: // CLIR temporary mode presentation allowed 1122 // the 'n' parameter from TS 27.007 7.7 1123 switch (clirArgs[0]) { 1124 default: 1125 case 0: // Default 1126 sb.append(context.getText( 1127 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1128 break; 1129 case 1: // CLIR invocation 1130 sb.append(context.getText( 1131 com.android.internal.R.string.CLIRDefaultOffNextCallOn)); 1132 break; 1133 case 2: // CLIR suppression 1134 sb.append(context.getText( 1135 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1136 break; 1137 } 1138 1139 state = State.COMPLETE; 1140 break; 1141 } 1142 } 1143 1144 message = sb; 1145 phone.onMMIDone(this); 1146 } 1147 1148 /** 1149 * @param serviceClass 1 bit of the service class bit vectory 1150 * @return String to be used for call forward query MMI response text. 1151 * Returns null if unrecognized 1152 */ 1153 1154 private CharSequence 1155 serviceClassToCFString (int serviceClass) { 1156 switch (serviceClass) { 1157 case SERVICE_CLASS_VOICE: 1158 return context.getText(com.android.internal.R.string.serviceClassVoice); 1159 case SERVICE_CLASS_DATA: 1160 return context.getText(com.android.internal.R.string.serviceClassData); 1161 case SERVICE_CLASS_FAX: 1162 return context.getText(com.android.internal.R.string.serviceClassFAX); 1163 case SERVICE_CLASS_SMS: 1164 return context.getText(com.android.internal.R.string.serviceClassSMS); 1165 case SERVICE_CLASS_DATA_SYNC: 1166 return context.getText(com.android.internal.R.string.serviceClassDataSync); 1167 case SERVICE_CLASS_DATA_ASYNC: 1168 return context.getText(com.android.internal.R.string.serviceClassDataAsync); 1169 case SERVICE_CLASS_PACKET: 1170 return context.getText(com.android.internal.R.string.serviceClassPacket); 1171 case SERVICE_CLASS_PAD: 1172 return context.getText(com.android.internal.R.string.serviceClassPAD); 1173 default: 1174 return null; 1175 } 1176 } 1177 1178 1179 /** one CallForwardInfo + serviceClassMask -> one line of text */ 1180 private CharSequence 1181 makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) { 1182 CharSequence template; 1183 String sources[] = {"{0}", "{1}", "{2}"}; 1184 CharSequence destinations[] = new CharSequence[3]; 1185 boolean needTimeTemplate; 1186 1187 // CF_REASON_NO_REPLY also has a time value associated with 1188 // it. All others don't. 1189 1190 needTimeTemplate = 1191 (info.reason == CommandsInterface.CF_REASON_NO_REPLY); 1192 1193 if (info.status == 1) { 1194 if (needTimeTemplate) { 1195 template = context.getText( 1196 com.android.internal.R.string.cfTemplateForwardedTime); 1197 } else { 1198 template = context.getText( 1199 com.android.internal.R.string.cfTemplateForwarded); 1200 } 1201 } else if (info.status == 0 && isEmptyOrNull(info.number)) { 1202 template = context.getText( 1203 com.android.internal.R.string.cfTemplateNotForwarded); 1204 } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */ 1205 // A call forward record that is not active but contains 1206 // a phone number is considered "registered" 1207 1208 if (needTimeTemplate) { 1209 template = context.getText( 1210 com.android.internal.R.string.cfTemplateRegisteredTime); 1211 } else { 1212 template = context.getText( 1213 com.android.internal.R.string.cfTemplateRegistered); 1214 } 1215 } 1216 1217 // In the template (from strings.xmls) 1218 // {0} is one of "bearerServiceCode*" 1219 // {1} is dialing number 1220 // {2} is time in seconds 1221 1222 destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask); 1223 destinations[1] = PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa); 1224 destinations[2] = Integer.toString(info.timeSeconds); 1225 1226 if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL && 1227 (info.serviceClass & serviceClassMask) 1228 == CommandsInterface.SERVICE_CLASS_VOICE) { 1229 boolean cffEnabled = (info.status == 1); 1230 if (mIccRecords != null) { 1231 mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled); 1232 } 1233 } 1234 1235 return TextUtils.replace(template, sources, destinations); 1236 } 1237 1238 1239 private void 1240 onQueryCfComplete(AsyncResult ar) { 1241 StringBuilder sb = new StringBuilder(getScString()); 1242 sb.append("\n"); 1243 1244 if (ar.exception != null) { 1245 state = State.FAILED; 1246 sb.append(getErrorMessage(ar)); 1247 } else { 1248 CallForwardInfo infos[]; 1249 1250 infos = (CallForwardInfo[]) ar.result; 1251 1252 if (infos.length == 0) { 1253 // Assume the default is not active 1254 sb.append(context.getText(com.android.internal.R.string.serviceDisabled)); 1255 1256 // Set unconditional CFF in SIM to false 1257 if (mIccRecords != null) { 1258 mIccRecords.setVoiceCallForwardingFlag(1, false); 1259 } 1260 } else { 1261 1262 SpannableStringBuilder tb = new SpannableStringBuilder(); 1263 1264 // Each bit in the service class gets its own result line 1265 // The service classes may be split up over multiple 1266 // CallForwardInfos. So, for each service class, find out 1267 // which CallForwardInfo represents it and then build 1268 // the response text based on that 1269 1270 for (int serviceClassMask = 1 1271 ; serviceClassMask <= SERVICE_CLASS_MAX 1272 ; serviceClassMask <<= 1 1273 ) { 1274 for (int i = 0, s = infos.length; i < s ; i++) { 1275 if ((serviceClassMask & infos[i].serviceClass) != 0) { 1276 tb.append(makeCFQueryResultMessage(infos[i], 1277 serviceClassMask)); 1278 tb.append("\n"); 1279 } 1280 } 1281 } 1282 sb.append(tb); 1283 } 1284 1285 state = State.COMPLETE; 1286 } 1287 1288 message = sb; 1289 phone.onMMIDone(this); 1290 1291 } 1292 1293 private void 1294 onQueryComplete(AsyncResult ar) { 1295 StringBuilder sb = new StringBuilder(getScString()); 1296 sb.append("\n"); 1297 1298 if (ar.exception != null) { 1299 state = State.FAILED; 1300 sb.append(getErrorMessage(ar)); 1301 } else { 1302 int[] ints = (int[])ar.result; 1303 1304 if (ints.length != 0) { 1305 if (ints[0] == 0) { 1306 sb.append(context.getText(com.android.internal.R.string.serviceDisabled)); 1307 } else if (sc.equals(SC_WAIT)) { 1308 // Call Waiting includes additional data in the response. 1309 sb.append(createQueryCallWaitingResultMessage(ints[1])); 1310 } else if (isServiceCodeCallBarring(sc)) { 1311 // ints[0] for Call Barring is a bit vector of services 1312 sb.append(createQueryCallBarringResultMessage(ints[0])); 1313 } else if (ints[0] == 1) { 1314 // for all other services, treat it as a boolean 1315 sb.append(context.getText(com.android.internal.R.string.serviceEnabled)); 1316 } else { 1317 sb.append(context.getText(com.android.internal.R.string.mmiError)); 1318 } 1319 } else { 1320 sb.append(context.getText(com.android.internal.R.string.mmiError)); 1321 } 1322 state = State.COMPLETE; 1323 } 1324 1325 message = sb; 1326 phone.onMMIDone(this); 1327 } 1328 1329 private CharSequence 1330 createQueryCallWaitingResultMessage(int serviceClass) { 1331 StringBuilder sb = 1332 new StringBuilder(context.getText(com.android.internal.R.string.serviceEnabledFor)); 1333 1334 for (int classMask = 1 1335 ; classMask <= SERVICE_CLASS_MAX 1336 ; classMask <<= 1 1337 ) { 1338 if ((classMask & serviceClass) != 0) { 1339 sb.append("\n"); 1340 sb.append(serviceClassToCFString(classMask & serviceClass)); 1341 } 1342 } 1343 return sb; 1344 } 1345 private CharSequence 1346 createQueryCallBarringResultMessage(int serviceClass) 1347 { 1348 StringBuilder sb = new StringBuilder(context.getText(com.android.internal.R.string.serviceEnabledFor)); 1349 1350 for (int classMask = 1 1351 ; classMask <= SERVICE_CLASS_MAX 1352 ; classMask <<= 1 1353 ) { 1354 if ((classMask & serviceClass) != 0) { 1355 sb.append("\n"); 1356 sb.append(serviceClassToCFString(classMask & serviceClass)); 1357 } 1358 } 1359 return sb; 1360 } 1361 1362 /*** 1363 * TODO: It would be nice to have a method here that can take in a dialstring and 1364 * figure out if there is an MMI code embedded within it. This code would replace 1365 * some of the string parsing functionality in the Phone App's 1366 * SpecialCharSequenceMgr class. 1367 */ 1368 1369 @Override 1370 public String toString() { 1371 StringBuilder sb = new StringBuilder("GsmMmiCode {"); 1372 1373 sb.append("State=" + getState()); 1374 if (action != null) sb.append(" action=" + action); 1375 if (sc != null) sb.append(" sc=" + sc); 1376 if (sia != null) sb.append(" sia=" + sia); 1377 if (sib != null) sb.append(" sib=" + sib); 1378 if (sic != null) sb.append(" sic=" + sic); 1379 if (poundString != null) sb.append(" poundString=" + poundString); 1380 if (dialingNumber != null) sb.append(" dialingNumber=" + dialingNumber); 1381 if (pwd != null) sb.append(" pwd=" + pwd); 1382 sb.append("}"); 1383 return sb.toString(); 1384 } 1385} 1386