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