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