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