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 710 String dialingNumber = mSia; 711 int reason = scToCallForwardReason(mSc); 712 int serviceClass = siToServiceClass(mSib); 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, serviceClass, 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 int serviceClass = siToServiceClass(mSib); 894 895 if (isActivate() || isDeactivate()) { 896 mPhone.setCallWaiting(isActivate(), serviceClass, 897 obtainMessage(EVENT_SET_COMPLETE, this)); 898 } else if (isInterrogate()) { 899 mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this)); 900 } else { 901 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 902 } 903 } else if (mPoundString != null) { 904 Rlog.d(LOG_TAG, "Sending pound string '" 905 + mDialingNumber + "' over CS pipe."); 906 throw new CallStateException(ImsPhone.CS_FALLBACK); 907 } else { 908 throw new RuntimeException ("Invalid or Unsupported MMI Code"); 909 } 910 } catch (RuntimeException exc) { 911 mState = State.FAILED; 912 mMessage = mContext.getText(com.android.internal.R.string.mmiError); 913 mPhone.onMMIDone(this); 914 } 915 } 916 917 /** 918 * Called from ImsPhone 919 * 920 * An unsolicited USSD NOTIFY or REQUEST has come in matching 921 * up with this pending USSD request 922 * 923 * Note: If REQUEST, this exchange is complete, but the session remains 924 * active (ie, the network expects user input). 925 */ 926 void 927 onUssdFinished(String ussdMessage, boolean isUssdRequest) { 928 if (mState == State.PENDING) { 929 if (ussdMessage == null) { 930 mMessage = mContext.getText(com.android.internal.R.string.mmiComplete); 931 } else { 932 mMessage = ussdMessage; 933 } 934 mIsUssdRequest = isUssdRequest; 935 // If it's a request, leave it PENDING so that it's cancelable. 936 if (!isUssdRequest) { 937 mState = State.COMPLETE; 938 } 939 940 mPhone.onMMIDone(this); 941 } 942 } 943 944 /** 945 * Called from ImsPhone 946 * 947 * The radio has reset, and this is still pending 948 */ 949 950 void 951 onUssdFinishedError() { 952 if (mState == State.PENDING) { 953 mState = State.FAILED; 954 mMessage = mContext.getText(com.android.internal.R.string.mmiError); 955 956 mPhone.onMMIDone(this); 957 } 958 } 959 960 void sendUssd(String ussdMessage) { 961 // Treat this as a USSD string 962 mIsPendingUSSD = true; 963 964 // Note that unlike most everything else, the USSD complete 965 // response does not complete this MMI code...we wait for 966 // an unsolicited USSD "Notify" or "Request". 967 // The matching up of this is done in ImsPhone. 968 969 mPhone.sendUSSD(ussdMessage, 970 obtainMessage(EVENT_USSD_COMPLETE, this)); 971 } 972 973 /** Called from ImsPhone.handleMessage; not a Handler subclass */ 974 @Override 975 public void 976 handleMessage (Message msg) { 977 AsyncResult ar; 978 979 switch (msg.what) { 980 case EVENT_SET_COMPLETE: 981 ar = (AsyncResult) (msg.obj); 982 983 onSetComplete(msg, ar); 984 break; 985 986 case EVENT_SET_CFF_COMPLETE: 987 ar = (AsyncResult) (msg.obj); 988 989 /* 990 * msg.arg1 = 1 means to set unconditional voice call forwarding 991 * msg.arg2 = 1 means to enable voice call forwarding 992 */ 993 if ((ar.exception == null) && (msg.arg1 == 1)) { 994 boolean cffEnabled = (msg.arg2 == 1); 995 if (mIccRecords != null) { 996 mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled, mDialingNumber); 997 } 998 } 999 1000 onSetComplete(msg, ar); 1001 break; 1002 1003 case EVENT_QUERY_CF_COMPLETE: 1004 ar = (AsyncResult) (msg.obj); 1005 onQueryCfComplete(ar); 1006 break; 1007 1008 case EVENT_QUERY_COMPLETE: 1009 ar = (AsyncResult) (msg.obj); 1010 onQueryComplete(ar); 1011 break; 1012 1013 case EVENT_USSD_COMPLETE: 1014 ar = (AsyncResult) (msg.obj); 1015 1016 if (ar.exception != null) { 1017 mState = State.FAILED; 1018 mMessage = getErrorMessage(ar); 1019 1020 mPhone.onMMIDone(this); 1021 } 1022 1023 // Note that unlike most everything else, the USSD complete 1024 // response does not complete this MMI code...we wait for 1025 // an unsolicited USSD "Notify" or "Request". 1026 // The matching up of this is done in ImsPhone. 1027 1028 break; 1029 1030 case EVENT_USSD_CANCEL_COMPLETE: 1031 mPhone.onMMIDone(this); 1032 break; 1033 1034 case EVENT_SUPP_SVC_QUERY_COMPLETE: 1035 ar = (AsyncResult) (msg.obj); 1036 onSuppSvcQueryComplete(ar); 1037 break; 1038 1039 case EVENT_GET_CLIR_COMPLETE: 1040 ar = (AsyncResult) (msg.obj); 1041 onQueryClirComplete(ar); 1042 break; 1043 1044 default: 1045 break; 1046 } 1047 } 1048 1049 //***** Private instance methods 1050 1051 private void 1052 processIcbMmiCodeForUpdate () { 1053 String dialingNumber = mSia; 1054 String[] icbNum = null; 1055 1056 if (dialingNumber != null) { 1057 icbNum = dialingNumber.split("\\$"); 1058 } 1059 1060 try { 1061 mPhone.mCT.getUtInterface() 1062 .updateCallBarring(ImsUtInterface.CB_BS_MT, 1063 isActivate(), 1064 obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE,this), 1065 icbNum); 1066 } catch (ImsException e) { 1067 Rlog.d(LOG_TAG, "Could not get UT handle for updating ICB."); 1068 } 1069 } 1070 1071 private CharSequence getErrorMessage(AsyncResult ar) { 1072 return mContext.getText(com.android.internal.R.string.mmiError); 1073 } 1074 1075 private CharSequence getScString() { 1076 if (mSc != null) { 1077 if (isServiceCodeCallBarring(mSc)) { 1078 return mContext.getText(com.android.internal.R.string.BaMmi); 1079 } else if (isServiceCodeCallForwarding(mSc)) { 1080 return mContext.getText(com.android.internal.R.string.CfMmi); 1081 } else if (mSc.equals(SC_PWD)) { 1082 return mContext.getText(com.android.internal.R.string.PwdMmi); 1083 } else if (mSc.equals(SC_WAIT)) { 1084 return mContext.getText(com.android.internal.R.string.CwMmi); 1085 } else if (mSc.equals(SC_CLIP)) { 1086 return mContext.getText(com.android.internal.R.string.ClipMmi); 1087 } else if (mSc.equals(SC_CLIR)) { 1088 return mContext.getText(com.android.internal.R.string.ClirMmi); 1089 } else if (mSc.equals(SC_COLP)) { 1090 return mContext.getText(com.android.internal.R.string.ColpMmi); 1091 } else if (mSc.equals(SC_COLR)) { 1092 return mContext.getText(com.android.internal.R.string.ColrMmi); 1093 } 1094 } 1095 1096 return ""; 1097 } 1098 1099 private void 1100 onSetComplete(Message msg, AsyncResult ar){ 1101 StringBuilder sb = new StringBuilder(getScString()); 1102 sb.append("\n"); 1103 1104 if (ar.exception != null) { 1105 mState = State.FAILED; 1106 1107 if (ar.exception instanceof CommandException) { 1108 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError(); 1109 if (err == CommandException.Error.PASSWORD_INCORRECT) { 1110 sb.append(mContext.getText( 1111 com.android.internal.R.string.passwordIncorrect)); 1112 } else { 1113 sb.append(mContext.getText( 1114 com.android.internal.R.string.mmiError)); 1115 } 1116 } else { 1117 ImsException error = (ImsException) ar.exception; 1118 if (error.getMessage() != null) { 1119 sb.append(error.getMessage()); 1120 } else { 1121 sb.append(getErrorMessage(ar)); 1122 } 1123 } 1124 } else if (isActivate()) { 1125 mState = State.COMPLETE; 1126 if (mIsCallFwdReg) { 1127 sb.append(mContext.getText( 1128 com.android.internal.R.string.serviceRegistered)); 1129 } else { 1130 sb.append(mContext.getText( 1131 com.android.internal.R.string.serviceEnabled)); 1132 } 1133 } else if (isDeactivate()) { 1134 mState = State.COMPLETE; 1135 sb.append(mContext.getText( 1136 com.android.internal.R.string.serviceDisabled)); 1137 } else if (isRegister()) { 1138 mState = State.COMPLETE; 1139 sb.append(mContext.getText( 1140 com.android.internal.R.string.serviceRegistered)); 1141 } else if (isErasure()) { 1142 mState = State.COMPLETE; 1143 sb.append(mContext.getText( 1144 com.android.internal.R.string.serviceErased)); 1145 } else { 1146 mState = State.FAILED; 1147 sb.append(mContext.getText( 1148 com.android.internal.R.string.mmiError)); 1149 } 1150 1151 mMessage = sb; 1152 mPhone.onMMIDone(this); 1153 } 1154 1155 /** 1156 * @param serviceClass 1 bit of the service class bit vectory 1157 * @return String to be used for call forward query MMI response text. 1158 * Returns null if unrecognized 1159 */ 1160 1161 private CharSequence 1162 serviceClassToCFString (int serviceClass) { 1163 switch (serviceClass) { 1164 case SERVICE_CLASS_VOICE: 1165 return mContext.getText(com.android.internal.R.string.serviceClassVoice); 1166 case SERVICE_CLASS_DATA: 1167 return mContext.getText(com.android.internal.R.string.serviceClassData); 1168 case SERVICE_CLASS_FAX: 1169 return mContext.getText(com.android.internal.R.string.serviceClassFAX); 1170 case SERVICE_CLASS_SMS: 1171 return mContext.getText(com.android.internal.R.string.serviceClassSMS); 1172 case SERVICE_CLASS_DATA_SYNC: 1173 return mContext.getText(com.android.internal.R.string.serviceClassDataSync); 1174 case SERVICE_CLASS_DATA_ASYNC: 1175 return mContext.getText(com.android.internal.R.string.serviceClassDataAsync); 1176 case SERVICE_CLASS_PACKET: 1177 return mContext.getText(com.android.internal.R.string.serviceClassPacket); 1178 case SERVICE_CLASS_PAD: 1179 return mContext.getText(com.android.internal.R.string.serviceClassPAD); 1180 default: 1181 return null; 1182 } 1183 } 1184 1185 /** one CallForwardInfo + serviceClassMask -> one line of text */ 1186 private CharSequence 1187 makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) { 1188 CharSequence template; 1189 String sources[] = {"{0}", "{1}", "{2}"}; 1190 CharSequence destinations[] = new CharSequence[3]; 1191 boolean needTimeTemplate; 1192 1193 // CF_REASON_NO_REPLY also has a time value associated with 1194 // it. All others don't. 1195 1196 needTimeTemplate = 1197 (info.reason == CommandsInterface.CF_REASON_NO_REPLY); 1198 1199 if (info.status == 1) { 1200 if (needTimeTemplate) { 1201 template = mContext.getText( 1202 com.android.internal.R.string.cfTemplateForwardedTime); 1203 } else { 1204 template = mContext.getText( 1205 com.android.internal.R.string.cfTemplateForwarded); 1206 } 1207 } else if (info.status == 0 && isEmptyOrNull(info.number)) { 1208 template = mContext.getText( 1209 com.android.internal.R.string.cfTemplateNotForwarded); 1210 } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */ 1211 // A call forward record that is not active but contains 1212 // a phone number is considered "registered" 1213 1214 if (needTimeTemplate) { 1215 template = mContext.getText( 1216 com.android.internal.R.string.cfTemplateRegisteredTime); 1217 } else { 1218 template = mContext.getText( 1219 com.android.internal.R.string.cfTemplateRegistered); 1220 } 1221 } 1222 1223 // In the template (from strings.xmls) 1224 // {0} is one of "bearerServiceCode*" 1225 // {1} is dialing number 1226 // {2} is time in seconds 1227 1228 destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask); 1229 destinations[1] = PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa); 1230 destinations[2] = Integer.toString(info.timeSeconds); 1231 1232 if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL && 1233 (info.serviceClass & serviceClassMask) 1234 == CommandsInterface.SERVICE_CLASS_VOICE) { 1235 boolean cffEnabled = (info.status == 1); 1236 if (mIccRecords != null) { 1237 mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled, info.number); 1238 } 1239 } 1240 1241 return TextUtils.replace(template, sources, destinations); 1242 } 1243 1244 1245 private void 1246 onQueryCfComplete(AsyncResult ar) { 1247 StringBuilder sb = new StringBuilder(getScString()); 1248 sb.append("\n"); 1249 1250 if (ar.exception != null) { 1251 mState = State.FAILED; 1252 1253 if (ar.exception instanceof ImsException) { 1254 ImsException error = (ImsException) ar.exception; 1255 if (error.getMessage() != null) { 1256 sb.append(error.getMessage()); 1257 } else { 1258 sb.append(getErrorMessage(ar)); 1259 } 1260 } 1261 else { 1262 sb.append(getErrorMessage(ar)); 1263 } 1264 } else { 1265 CallForwardInfo infos[]; 1266 1267 infos = (CallForwardInfo[]) ar.result; 1268 1269 if (infos.length == 0) { 1270 // Assume the default is not active 1271 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1272 1273 // Set unconditional CFF in SIM to false 1274 if (mIccRecords != null) { 1275 mIccRecords.setVoiceCallForwardingFlag(1, false, null); 1276 } 1277 } else { 1278 1279 SpannableStringBuilder tb = new SpannableStringBuilder(); 1280 1281 // Each bit in the service class gets its own result line 1282 // The service classes may be split up over multiple 1283 // CallForwardInfos. So, for each service class, find out 1284 // which CallForwardInfo represents it and then build 1285 // the response text based on that 1286 1287 for (int serviceClassMask = 1 1288 ; serviceClassMask <= SERVICE_CLASS_MAX 1289 ; serviceClassMask <<= 1 1290 ) { 1291 for (int i = 0, s = infos.length; i < s ; i++) { 1292 if ((serviceClassMask & infos[i].serviceClass) != 0) { 1293 tb.append(makeCFQueryResultMessage(infos[i], 1294 serviceClassMask)); 1295 tb.append("\n"); 1296 } 1297 } 1298 } 1299 sb.append(tb); 1300 } 1301 1302 mState = State.COMPLETE; 1303 } 1304 1305 mMessage = sb; 1306 mPhone.onMMIDone(this); 1307 1308 } 1309 1310 private void onSuppSvcQueryComplete(AsyncResult ar) { 1311 StringBuilder sb = new StringBuilder(getScString()); 1312 sb.append("\n"); 1313 1314 if (ar.exception != null) { 1315 mState = State.FAILED; 1316 1317 if (ar.exception instanceof ImsException) { 1318 ImsException error = (ImsException) ar.exception; 1319 if (error.getMessage() != null) { 1320 sb.append(error.getMessage()); 1321 } else { 1322 sb.append(getErrorMessage(ar)); 1323 } 1324 } else { 1325 sb.append(getErrorMessage(ar)); 1326 } 1327 } else { 1328 mState = State.FAILED; 1329 ImsSsInfo ssInfo = null; 1330 if (ar.result instanceof Bundle) { 1331 Rlog.d(LOG_TAG, "Received CLIP/COLP/COLR Response."); 1332 // Response for CLIP, COLP and COLR queries. 1333 Bundle ssInfoResp = (Bundle) ar.result; 1334 ssInfo = (ImsSsInfo) ssInfoResp.getParcelable(UT_BUNDLE_KEY_SSINFO); 1335 if (ssInfo != null) { 1336 Rlog.d(LOG_TAG, "ImsSsInfo mStatus = " + ssInfo.mStatus); 1337 if (ssInfo.mStatus == ImsSsInfo.DISABLED) { 1338 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1339 mState = State.COMPLETE; 1340 } else if (ssInfo.mStatus == ImsSsInfo.ENABLED) { 1341 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1342 mState = State.COMPLETE; 1343 } else { 1344 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1345 } 1346 } else { 1347 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1348 } 1349 1350 } else { 1351 Rlog.d(LOG_TAG, "Received Call Barring Response."); 1352 // Response for Call Barring queries. 1353 int[] cbInfos = (int[]) ar.result; 1354 // Check if ImsPhone has received call barring 1355 // enabled for service class voice. 1356 if (cbInfos[0] == 1) { 1357 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1358 mState = State.COMPLETE; 1359 } else { 1360 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1361 mState = State.COMPLETE; 1362 } 1363 } 1364 1365 } 1366 1367 mMessage = sb; 1368 mPhone.onMMIDone(this); 1369 } 1370 1371 private void onQueryClirComplete(AsyncResult ar) { 1372 StringBuilder sb = new StringBuilder(getScString()); 1373 sb.append("\n"); 1374 mState = State.FAILED; 1375 1376 if (ar.exception != null) { 1377 1378 if (ar.exception instanceof ImsException) { 1379 ImsException error = (ImsException) ar.exception; 1380 if (error.getMessage() != null) { 1381 sb.append(error.getMessage()); 1382 } else { 1383 sb.append(getErrorMessage(ar)); 1384 } 1385 } 1386 } else { 1387 Bundle ssInfo = (Bundle) ar.result; 1388 int[] clirInfo = ssInfo.getIntArray(UT_BUNDLE_KEY_CLIR); 1389 // clirInfo[0] = The 'n' parameter from TS 27.007 7.7 1390 // clirInfo[1] = The 'm' parameter from TS 27.007 7.7 1391 Rlog.d(LOG_TAG, "CLIR param n=" + clirInfo[0] 1392 + " m=" + clirInfo[1]); 1393 1394 // 'm' parameter. 1395 switch (clirInfo[1]) { 1396 case CLIR_NOT_PROVISIONED: 1397 sb.append(mContext.getText( 1398 com.android.internal.R.string.serviceNotProvisioned)); 1399 mState = State.COMPLETE; 1400 break; 1401 case CLIR_PROVISIONED_PERMANENT: 1402 sb.append(mContext.getText( 1403 com.android.internal.R.string.CLIRPermanent)); 1404 mState = State.COMPLETE; 1405 break; 1406 case CLIR_PRESENTATION_RESTRICTED_TEMPORARY: 1407 // 'n' parameter. 1408 switch (clirInfo[0]) { 1409 case CLIR_DEFAULT: 1410 sb.append(mContext.getText( 1411 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1412 mState = State.COMPLETE; 1413 break; 1414 case CLIR_INVOCATION: 1415 sb.append(mContext.getText( 1416 com.android.internal.R.string.CLIRDefaultOnNextCallOn)); 1417 mState = State.COMPLETE; 1418 break; 1419 case CLIR_SUPPRESSION: 1420 sb.append(mContext.getText( 1421 com.android.internal.R.string.CLIRDefaultOnNextCallOff)); 1422 mState = State.COMPLETE; 1423 break; 1424 default: 1425 sb.append(mContext.getText( 1426 com.android.internal.R.string.mmiError)); 1427 mState = State.FAILED; 1428 } 1429 break; 1430 case CLIR_PRESENTATION_ALLOWED_TEMPORARY: 1431 // 'n' parameter. 1432 switch (clirInfo[0]) { 1433 case CLIR_DEFAULT: 1434 sb.append(mContext.getText( 1435 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1436 mState = State.COMPLETE; 1437 break; 1438 case CLIR_INVOCATION: 1439 sb.append(mContext.getText( 1440 com.android.internal.R.string.CLIRDefaultOffNextCallOn)); 1441 mState = State.COMPLETE; 1442 break; 1443 case CLIR_SUPPRESSION: 1444 sb.append(mContext.getText( 1445 com.android.internal.R.string.CLIRDefaultOffNextCallOff)); 1446 mState = State.COMPLETE; 1447 break; 1448 default: 1449 sb.append(mContext.getText( 1450 com.android.internal.R.string.mmiError)); 1451 mState = State.FAILED; 1452 } 1453 break; 1454 default: 1455 sb.append(mContext.getText( 1456 com.android.internal.R.string.mmiError)); 1457 mState = State.FAILED; 1458 } 1459 } 1460 1461 mMessage = sb; 1462 mPhone.onMMIDone(this); 1463 } 1464 1465 private void 1466 onQueryComplete(AsyncResult ar) { 1467 StringBuilder sb = new StringBuilder(getScString()); 1468 sb.append("\n"); 1469 1470 if (ar.exception != null) { 1471 mState = State.FAILED; 1472 1473 if (ar.exception instanceof ImsException) { 1474 ImsException error = (ImsException) ar.exception; 1475 if (error.getMessage() != null) { 1476 sb.append(error.getMessage()); 1477 } else { 1478 sb.append(getErrorMessage(ar)); 1479 } 1480 } else { 1481 sb.append(getErrorMessage(ar)); 1482 } 1483 1484 } else { 1485 int[] ints = (int[])ar.result; 1486 1487 if (ints.length != 0) { 1488 if (ints[0] == 0) { 1489 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled)); 1490 } else if (mSc.equals(SC_WAIT)) { 1491 // Call Waiting includes additional data in the response. 1492 sb.append(createQueryCallWaitingResultMessage(ints[1])); 1493 } else if (ints[0] == 1) { 1494 // for all other services, treat it as a boolean 1495 sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled)); 1496 } else { 1497 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1498 } 1499 } else { 1500 sb.append(mContext.getText(com.android.internal.R.string.mmiError)); 1501 } 1502 mState = State.COMPLETE; 1503 } 1504 1505 mMessage = sb; 1506 mPhone.onMMIDone(this); 1507 } 1508 1509 private CharSequence 1510 createQueryCallWaitingResultMessage(int serviceClass) { 1511 StringBuilder sb = new StringBuilder( 1512 mContext.getText(com.android.internal.R.string.serviceEnabledFor)); 1513 1514 for (int classMask = 1 1515 ; classMask <= SERVICE_CLASS_MAX 1516 ; classMask <<= 1 1517 ) { 1518 if ((classMask & serviceClass) != 0) { 1519 sb.append("\n"); 1520 sb.append(serviceClassToCFString(classMask & serviceClass)); 1521 } 1522 } 1523 return sb; 1524 } 1525 1526 /*** 1527 * TODO: It would be nice to have a method here that can take in a dialstring and 1528 * figure out if there is an MMI code embedded within it. This code would replace 1529 * some of the string parsing functionality in the Phone App's 1530 * SpecialCharSequenceMgr class. 1531 */ 1532 1533 @Override 1534 public String toString() { 1535 StringBuilder sb = new StringBuilder("ImsPhoneMmiCode {"); 1536 1537 sb.append("State=" + getState()); 1538 if (mAction != null) sb.append(" action=" + mAction); 1539 if (mSc != null) sb.append(" sc=" + mSc); 1540 if (mSia != null) sb.append(" sia=" + mSia); 1541 if (mSib != null) sb.append(" sib=" + mSib); 1542 if (mSic != null) sb.append(" sic=" + mSic); 1543 if (mPoundString != null) sb.append(" poundString=" + mPoundString); 1544 if (mDialingNumber != null) sb.append(" dialingNumber=" + mDialingNumber); 1545 if (mPwd != null) sb.append(" pwd=" + mPwd); 1546 sb.append("}"); 1547 return sb.toString(); 1548 } 1549 } 1550