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