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