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