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