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