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