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 isPinPukCommand() { 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 (isPinPukCommand()) { 783 // TODO: This is the same as the code in CmdaMmiCode.java, 784 // MmiCode should be an abstract or base class and this and 785 // other common variables and code should be promoted. 786 787 // sia = old PIN or PUK 788 // sib = new PIN 789 // sic = new PIN 790 String oldPinOrPuk = mSia; 791 String newPinOrPuk = mSib; 792 int pinLen = newPinOrPuk.length(); 793 if (isRegister()) { 794 if (!newPinOrPuk.equals(mSic)) { 795 // password mismatch; return error 796 handlePasswordError(com.android.internal.R.string.mismatchPin); 797 } else if (pinLen < 4 || pinLen > 8 ) { 798 // invalid length 799 handlePasswordError(com.android.internal.R.string.invalidPin); 800 } else if (mSc.equals(SC_PIN) 801 && mUiccApplication != null 802 && mUiccApplication.getState() == AppState.APPSTATE_PUK) { 803 // Sim is puk-locked 804 handlePasswordError(com.android.internal.R.string.needPuk); 805 } else if (mUiccApplication != null) { 806 Rlog.d(LOG_TAG, "process mmi service code using UiccApp sc=" + mSc); 807 808 // We have an app and the pre-checks are OK 809 if (mSc.equals(SC_PIN)) { 810 mUiccApplication.changeIccLockPassword(oldPinOrPuk, newPinOrPuk, 811 obtainMessage(EVENT_SET_COMPLETE, this)); 812 } else if (mSc.equals(SC_PIN2)) { 813 mUiccApplication.changeIccFdnPassword(oldPinOrPuk, newPinOrPuk, 814 obtainMessage(EVENT_SET_COMPLETE, this)); 815 } else if (mSc.equals(SC_PUK)) { 816 mUiccApplication.supplyPuk(oldPinOrPuk, newPinOrPuk, 817 obtainMessage(EVENT_SET_COMPLETE, this)); 818 } else if (mSc.equals(SC_PUK2)) { 819 mUiccApplication.supplyPuk2(oldPinOrPuk, newPinOrPuk, 820 obtainMessage(EVENT_SET_COMPLETE, this)); 821 } else { 822 throw new RuntimeException("uicc unsupported service code=" + mSc); 823 } 824 } else { 825 throw new RuntimeException("No application mUiccApplicaiton is null"); 826 } 827 } else { 828 throw new RuntimeException ("Ivalid register/action=" + mAction); 829 } 830 } else if (mPoundString != null) { 831 sendUssd(mPoundString); 832 } else { 833 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 834 } 835 } catch (RuntimeException exc) { 836 mState = State.FAILED; 837 mMessage = mContext.getText(com.android.internal.R.string.mmiError); 838 mPhone.onMMIDone(this); 839 } 840 } 841 842 private void handlePasswordError(int res) { 843 mState = State.FAILED; 844 StringBuilder sb = new StringBuilder(getScString()); 845 sb.append("\n"); 846 sb.append(mContext.getText(res)); 847 mMessage = sb; 848 mPhone.onMMIDone(this); 849 } 850 851 /** 852 * Called from GSMPhone 853 * 854 * An unsolicited USSD NOTIFY or REQUEST has come in matching 855 * up with this pending USSD request 856 * 857 * Note: If REQUEST, this exchange is complete, but the session remains 858 * active (ie, the network expects user input). 859 */ 860 void 861 onUssdFinished(String ussdMessage, boolean isUssdRequest) { 862 if (mState == State.PENDING) { 863 if (ussdMessage == null) { 864 mMessage = mContext.getText(com.android.internal.R.string.mmiComplete); 865 } else { 866 mMessage = ussdMessage; 867 } 868 mIsUssdRequest = isUssdRequest; 869 // If it's a request, leave it PENDING so that it's cancelable. 870 if (!isUssdRequest) { 871 mState = State.COMPLETE; 872 } 873 874 mPhone.onMMIDone(this); 875 } 876 } 877 878 /** 879 * Called from GSMPhone 880 * 881 * The radio has reset, and this is still pending 882 */ 883 884 void 885 onUssdFinishedError() { 886 if (mState == State.PENDING) { 887 mState = State.FAILED; 888 mMessage = mContext.getText(com.android.internal.R.string.mmiError); 889 890 mPhone.onMMIDone(this); 891 } 892 } 893 894 void sendUssd(String ussdMessage) { 895 // Treat this as a USSD string 896 mIsPendingUSSD = true; 897 898 // Note that unlike most everything else, the USSD complete 899 // response does not complete this MMI code...we wait for 900 // an unsolicited USSD "Notify" or "Request". 901 // The matching up of this is done in GSMPhone. 902 903 mPhone.mCi.sendUSSD(ussdMessage, 904 obtainMessage(EVENT_USSD_COMPLETE, this)); 905 } 906 907 /** Called from GSMPhone.handleMessage; not a Handler subclass */ 908 @Override 909 public void 910 handleMessage (Message msg) { 911 AsyncResult ar; 912 913 switch (msg.what) { 914 case EVENT_SET_COMPLETE: 915 ar = (AsyncResult) (msg.obj); 916 917 onSetComplete(msg, ar); 918 break; 919 920 case EVENT_SET_CFF_COMPLETE: 921 ar = (AsyncResult) (msg.obj); 922 923 /* 924 * msg.arg1 = 1 means to set unconditional voice call forwarding 925 * msg.arg2 = 1 means to enable voice call forwarding 926 */ 927 if ((ar.exception == null) && (msg.arg1 == 1)) { 928 boolean cffEnabled = (msg.arg2 == 1); 929 if (mIccRecords != null) { 930 mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled, mDialingNumber); 931 } 932 } 933 934 onSetComplete(msg, ar); 935 break; 936 937 case EVENT_GET_CLIR_COMPLETE: 938 ar = (AsyncResult) (msg.obj); 939 onGetClirComplete(ar); 940 break; 941 942 case EVENT_QUERY_CF_COMPLETE: 943 ar = (AsyncResult) (msg.obj); 944 onQueryCfComplete(ar); 945 break; 946 947 case EVENT_QUERY_COMPLETE: 948 ar = (AsyncResult) (msg.obj); 949 onQueryComplete(ar); 950 break; 951 952 case EVENT_USSD_COMPLETE: 953 ar = (AsyncResult) (msg.obj); 954 955 if (ar.exception != null) { 956 mState = State.FAILED; 957 mMessage = getErrorMessage(ar); 958 959 mPhone.onMMIDone(this); 960 } 961 962 // Note that unlike most everything else, the USSD complete 963 // response does not complete this MMI code...we wait for 964 // an unsolicited USSD "Notify" or "Request". 965 // The matching up of this is done in GSMPhone. 966 967 break; 968 969 case EVENT_USSD_CANCEL_COMPLETE: 970 mPhone.onMMIDone(this); 971 break; 972 } 973 } 974 //***** Private instance methods 975 976 private CharSequence getErrorMessage(AsyncResult ar) { 977 978 if (ar.exception instanceof CommandException) { 979 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); 980 if (err == CommandException.Error.FDN_CHECK_FAILURE) { 981 Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE"); 982 return mContext.getText(com.android.internal.R.string.mmiFdnError); 983 } 984 } 985 986 return mContext.getText(com.android.internal.R.string.mmiError); 987 } 988 989 private CharSequence getScString() { 990 if (mSc != null) { 991 if (isServiceCodeCallBarring(mSc)) { 992 return mContext.getText(com.android.internal.R.string.BaMmi); 993 } else if (isServiceCodeCallForwarding(mSc)) { 994 return mContext.getText(com.android.internal.R.string.CfMmi); 995 } else if (mSc.equals(SC_CLIP)) { 996 return mContext.getText(com.android.internal.R.string.ClipMmi); 997 } else if (mSc.equals(SC_CLIR)) { 998 return mContext.getText(com.android.internal.R.string.ClirMmi); 999 } else if (mSc.equals(SC_PWD)) { 1000 return mContext.getText(com.android.internal.R.string.PwdMmi); 1001 } else if (mSc.equals(SC_WAIT)) { 1002 return mContext.getText(com.android.internal.R.string.CwMmi); 1003 } else if (isPinPukCommand()) { 1004 return mContext.getText(com.android.internal.R.string.PinMmi); 1005 } 1006 } 1007 1008 return ""; 1009 } 1010 1011 private void 1012 onSetComplete(Message msg, AsyncResult ar){ 1013 StringBuilder sb = new StringBuilder(getScString()); 1014 sb.append("\n"); 1015 1016 if (ar.exception != null) { 1017 mState = State.FAILED; 1018 if (ar.exception instanceof CommandException) { 1019 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); 1020 if (err == CommandException.Error.PASSWORD_INCORRECT) { 1021 if (isPinPukCommand()) { 1022 // look specifically for the PUK commands and adjust 1023 // the message accordingly. 1024 if (mSc.equals(SC_PUK) || mSc.equals(SC_PUK2)) { 1025 sb.append(mContext.getText( 1026 com.android.internal.R.string.badPuk)); 1027 } else { 1028 sb.append(mContext.getText( 1029 com.android.internal.R.string.badPin)); 1030 } 1031 // Get the No. of retries remaining to unlock PUK/PUK2 1032 int attemptsRemaining = msg.arg1; 1033 if (attemptsRemaining <= 0) { 1034 Rlog.d(LOG_TAG, "onSetComplete: PUK locked," 1035 + " cancel as lock screen will handle this"); 1036 mState = State.CANCELLED; 1037 } else if (attemptsRemaining > 0) { 1038 Rlog.d(LOG_TAG, "onSetComplete: attemptsRemaining="+attemptsRemaining); 1039 sb.append(mContext.getResources().getQuantityString( 1040 com.android.internal.R.plurals.pinpuk_attempts, 1041 attemptsRemaining, attemptsRemaining)); 1042 } 1043 } else { 1044 sb.append(mContext.getText( 1045 com.android.internal.R.string.passwordIncorrect)); 1046 } 1047 } else if (err == CommandException.Error.SIM_PUK2) { 1048 sb.append(mContext.getText( 1049 com.android.internal.R.string.badPin)); 1050 sb.append("\n"); 1051 sb.append(mContext.getText( 1052 com.android.internal.R.string.needPuk2)); 1053 } else if (err == CommandException.Error.REQUEST_NOT_SUPPORTED) { 1054 if (mSc.equals(SC_PIN)) { 1055 sb.append(mContext.getText(com.android.internal.R.string.enablePin)); 1056 } 1057 } else if (err == CommandException.Error.FDN_CHECK_FAILURE) { 1058 Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE"); 1059 sb.append(mContext.getText(com.android.internal.R.string.mmiFdnError)); 1060 } else { 1061 sb.append(mContext.getText( 1062 com.android.internal.R.string.mmiError)); 1063 } 1064 } else { 1065 sb.append(mContext.getText( 1066 com.android.internal.R.string.mmiError)); 1067 } 1068 } else if (isActivate()) { 1069 mState = State.COMPLETE; 1070 if (mIsCallFwdReg) { 1071 sb.append(mContext.getText( 1072 com.android.internal.R.string.serviceRegistered)); 1073 } else { 1074 sb.append(mContext.getText( 1075 com.android.internal.R.string.serviceEnabled)); 1076 } 1077 // Record CLIR setting 1078 if (mSc.equals(SC_CLIR)) { 1079 mPhone.saveClirSetting(CommandsInterface.CLIR_INVOCATION); 1080 } 1081 } else if (isDeactivate()) { 1082 mState = State.COMPLETE; 1083 sb.append(mContext.getText( 1084 com.android.internal.R.string.serviceDisabled)); 1085 // Record CLIR setting 1086 if (mSc.equals(SC_CLIR)) { 1087 mPhone.saveClirSetting(CommandsInterface.CLIR_SUPPRESSION); 1088 } 1089 } else if (isRegister()) { 1090 mState = State.COMPLETE; 1091 sb.append(mContext.getText( 1092 com.android.internal.R.string.serviceRegistered)); 1093 } else if (isErasure()) { 1094 mState = State.COMPLETE; 1095 sb.append(mContext.getText( 1096 com.android.internal.R.string.serviceErased)); 1097 } else { 1098 mState = State.FAILED; 1099 sb.append(mContext.getText( 1100 com.android.internal.R.string.mmiError)); 1101 } 1102 1103 mMessage = sb; 1104 mPhone.onMMIDone(this); 1105 } 1106 1107 private void 1108 onGetClirComplete(AsyncResult ar) { 1109 StringBuilder sb = new StringBuilder(getScString()); 1110 sb.append("\n"); 1111 1112 if (ar.exception != null) { 1113 mState = State.FAILED; 1114 sb.append(getErrorMessage(ar)); 1115 } else { 1116 int clirArgs[]; 1117 1118 clirArgs = (int[])ar.result; 1119 1120 // the 'm' parameter from TS 27.007 7.7 1121 switch (clirArgs[1]) { 1122 case 0: // CLIR not provisioned 1123 sb.append(mContext.getText( 1124 com.android.internal.R.string.serviceNotProvisioned)); 1125 mState = State.COMPLETE; 1126 break; 1127 1128 case 1: // CLIR provisioned in permanent mode 1129 sb.append(mContext.getText( 1130 com.android.internal.R.string.CLIRPermanent)); 1131 mState = State.COMPLETE; 1132 break; 1133 1134 case 2: // unknown (e.g. no network, etc.) 1135 sb.append(mContext.getText( 1136 com.android.internal.R.string.mmiError)); 1137 mState = State.FAILED; 1138 break; 1139 1140 case 3: // CLIR temporary mode presentation restricted 1141 1142 // the 'n' parameter from TS 27.007 7.7 1143 switch (clirArgs[0]) { 1144 default: 1145 case 0: // Default 1146 sb.append(mContext.getText( 1147 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1148 break; 1149 case 1: // CLIR invocation 1150 sb.append(mContext.getText( 1151 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1152 break; 1153 case 2: // CLIR suppression 1154 sb.append(mContext.getText( 1155 com.android.internal.R.string.CLIRDefaultOnNextCallOff)); 1156 break; 1157 } 1158 mState = State.COMPLETE; 1159 break; 1160 1161 case 4: // CLIR temporary mode presentation allowed 1162 // the 'n' parameter from TS 27.007 7.7 1163 switch (clirArgs[0]) { 1164 default: 1165 case 0: // Default 1166 sb.append(mContext.getText( 1167 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1168 break; 1169 case 1: // CLIR invocation 1170 sb.append(mContext.getText( 1171 com.android.internal.R.string.CLIRDefaultOffNextCallOn)); 1172 break; 1173 case 2: // CLIR suppression 1174 sb.append(mContext.getText( 1175 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1176 break; 1177 } 1178 1179 mState = State.COMPLETE; 1180 break; 1181 } 1182 } 1183 1184 mMessage = sb; 1185 mPhone.onMMIDone(this); 1186 } 1187 1188 /** 1189 * @param serviceClass 1 bit of the service class bit vectory 1190 * @return String to be used for call forward query MMI response text. 1191 * Returns null if unrecognized 1192 */ 1193 1194 private CharSequence 1195 serviceClassToCFString (int serviceClass) { 1196 switch (serviceClass) { 1197 case SERVICE_CLASS_VOICE: 1198 return mContext.getText(com.android.internal.R.string.serviceClassVoice); 1199 case SERVICE_CLASS_DATA: 1200 return mContext.getText(com.android.internal.R.string.serviceClassData); 1201 case SERVICE_CLASS_FAX: 1202 return mContext.getText(com.android.internal.R.string.serviceClassFAX); 1203 case SERVICE_CLASS_SMS: 1204 return mContext.getText(com.android.internal.R.string.serviceClassSMS); 1205 case SERVICE_CLASS_DATA_SYNC: 1206 return mContext.getText(com.android.internal.R.string.serviceClassDataSync); 1207 case SERVICE_CLASS_DATA_ASYNC: 1208 return mContext.getText(com.android.internal.R.string.serviceClassDataAsync); 1209 case SERVICE_CLASS_PACKET: 1210 return mContext.getText(com.android.internal.R.string.serviceClassPacket); 1211 case SERVICE_CLASS_PAD: 1212 return mContext.getText(com.android.internal.R.string.serviceClassPAD); 1213 default: 1214 return null; 1215 } 1216 } 1217 1218 1219 /** one CallForwardInfo + serviceClassMask -> one line of text */ 1220 private CharSequence 1221 makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) { 1222 CharSequence template; 1223 String sources[] = {"{0}", "{1}", "{2}"}; 1224 CharSequence destinations[] = new CharSequence[3]; 1225 boolean needTimeTemplate; 1226 1227 // CF_REASON_NO_REPLY also has a time value associated with 1228 // it. All others don't. 1229 1230 needTimeTemplate = 1231 (info.reason == CommandsInterface.CF_REASON_NO_REPLY); 1232 1233 if (info.status == 1) { 1234 if (needTimeTemplate) { 1235 template = mContext.getText( 1236 com.android.internal.R.string.cfTemplateForwardedTime); 1237 } else { 1238 template = mContext.getText( 1239 com.android.internal.R.string.cfTemplateForwarded); 1240 } 1241 } else if (info.status == 0 && isEmptyOrNull(info.number)) { 1242 template = mContext.getText( 1243 com.android.internal.R.string.cfTemplateNotForwarded); 1244 } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */ 1245 // A call forward record that is not active but contains 1246 // a phone number is considered "registered" 1247 1248 if (needTimeTemplate) { 1249 template = mContext.getText( 1250 com.android.internal.R.string.cfTemplateRegisteredTime); 1251 } else { 1252 template = mContext.getText( 1253 com.android.internal.R.string.cfTemplateRegistered); 1254 } 1255 } 1256 1257 // In the template (from strings.xmls) 1258 // {0} is one of "bearerServiceCode*" 1259 // {1} is dialing number 1260 // {2} is time in seconds 1261 1262 destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask); 1263 destinations[1] = PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa); 1264 destinations[2] = Integer.toString(info.timeSeconds); 1265 1266 if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL && 1267 (info.serviceClass & serviceClassMask) 1268 == CommandsInterface.SERVICE_CLASS_VOICE) { 1269 boolean cffEnabled = (info.status == 1); 1270 if (mIccRecords != null) { 1271 mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled, info.number); 1272 } 1273 } 1274 1275 return TextUtils.replace(template, sources, destinations); 1276 } 1277 1278 1279 private void 1280 onQueryCfComplete(AsyncResult ar) { 1281 StringBuilder sb = new StringBuilder(getScString()); 1282 sb.append("\n"); 1283 1284 if (ar.exception != null) { 1285 mState = State.FAILED; 1286 sb.append(getErrorMessage(ar)); 1287 } else { 1288 CallForwardInfo infos[]; 1289 1290 infos = (CallForwardInfo[]) ar.result; 1291 1292 if (infos.length == 0) { 1293 // Assume the default is not active 1294 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1295 1296 // Set unconditional CFF in SIM to false 1297 if (mIccRecords != null) { 1298 mIccRecords.setVoiceCallForwardingFlag(1, false, null); 1299 } 1300 } else { 1301 1302 SpannableStringBuilder tb = new SpannableStringBuilder(); 1303 1304 // Each bit in the service class gets its own result line 1305 // The service classes may be split up over multiple 1306 // CallForwardInfos. So, for each service class, find out 1307 // which CallForwardInfo represents it and then build 1308 // the response text based on that 1309 1310 for (int serviceClassMask = 1 1311 ; serviceClassMask <= SERVICE_CLASS_MAX 1312 ; serviceClassMask <<= 1 1313 ) { 1314 for (int i = 0, s = infos.length; i < s ; i++) { 1315 if ((serviceClassMask & infos[i].serviceClass) != 0) { 1316 tb.append(makeCFQueryResultMessage(infos[i], 1317 serviceClassMask)); 1318 tb.append("\n"); 1319 } 1320 } 1321 } 1322 sb.append(tb); 1323 } 1324 1325 mState = State.COMPLETE; 1326 } 1327 1328 mMessage = sb; 1329 mPhone.onMMIDone(this); 1330 1331 } 1332 1333 private void 1334 onQueryComplete(AsyncResult ar) { 1335 StringBuilder sb = new StringBuilder(getScString()); 1336 sb.append("\n"); 1337 1338 if (ar.exception != null) { 1339 mState = State.FAILED; 1340 sb.append(getErrorMessage(ar)); 1341 } else { 1342 int[] ints = (int[])ar.result; 1343 1344 if (ints.length != 0) { 1345 if (ints[0] == 0) { 1346 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1347 } else if (mSc.equals(SC_WAIT)) { 1348 // Call Waiting includes additional data in the response. 1349 sb.append(createQueryCallWaitingResultMessage(ints[1])); 1350 } else if (isServiceCodeCallBarring(mSc)) { 1351 // ints[0] for Call Barring is a bit vector of services 1352 sb.append(createQueryCallBarringResultMessage(ints[0])); 1353 } else if (ints[0] == 1) { 1354 // for all other services, treat it as a boolean 1355 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1356 } else { 1357 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1358 } 1359 } else { 1360 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1361 } 1362 mState = State.COMPLETE; 1363 } 1364 1365 mMessage = sb; 1366 mPhone.onMMIDone(this); 1367 } 1368 1369 private CharSequence 1370 createQueryCallWaitingResultMessage(int serviceClass) { 1371 StringBuilder sb = 1372 new StringBuilder(mContext.getText(com.android.internal.R.string.serviceEnabledFor)); 1373 1374 for (int classMask = 1 1375 ; classMask <= SERVICE_CLASS_MAX 1376 ; classMask <<= 1 1377 ) { 1378 if ((classMask & serviceClass) != 0) { 1379 sb.append("\n"); 1380 sb.append(serviceClassToCFString(classMask & serviceClass)); 1381 } 1382 } 1383 return sb; 1384 } 1385 private CharSequence 1386 createQueryCallBarringResultMessage(int serviceClass) 1387 { 1388 StringBuilder sb = new StringBuilder(mContext.getText(com.android.internal.R.string.serviceEnabledFor)); 1389 1390 for (int classMask = 1 1391 ; classMask <= SERVICE_CLASS_MAX 1392 ; classMask <<= 1 1393 ) { 1394 if ((classMask & serviceClass) != 0) { 1395 sb.append("\n"); 1396 sb.append(serviceClassToCFString(classMask & serviceClass)); 1397 } 1398 } 1399 return sb; 1400 } 1401 1402 /*** 1403 * TODO: It would be nice to have a method here that can take in a dialstring and 1404 * figure out if there is an MMI code embedded within it. This code would replace 1405 * some of the string parsing functionality in the Phone App's 1406 * SpecialCharSequenceMgr class. 1407 */ 1408 1409 @Override 1410 public String toString() { 1411 StringBuilder sb = new StringBuilder("GsmMmiCode {"); 1412 1413 sb.append("State=" + getState()); 1414 if (mAction != null) sb.append(" action=" + mAction); 1415 if (mSc != null) sb.append(" sc=" + mSc); 1416 if (mSia != null) sb.append(" sia=" + mSia); 1417 if (mSib != null) sb.append(" sib=" + mSib); 1418 if (mSic != null) sb.append(" sic=" + mSic); 1419 if (mPoundString != null) sb.append(" poundString=" + mPoundString); 1420 if (mDialingNumber != null) sb.append(" dialingNumber=" + mDialingNumber); 1421 if (mPwd != null) sb.append(" pwd=" + mPwd); 1422 sb.append("}"); 1423 return sb.toString(); 1424 } 1425 } 1426