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