1 /*
2  * Copyright (C) 2007 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.cat;
18 
19 import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.BROWSER_TERMINATION_EVENT;
20 import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.BROWSING_STATUS_EVENT;
21 import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT;
22 import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.LANGUAGE_SELECTION_EVENT;
23 import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.USER_ACTIVITY_EVENT;
24 
25 import android.compat.annotation.UnsupportedAppUsage;
26 import android.content.Context;
27 import android.content.res.Resources.NotFoundException;
28 import android.graphics.Bitmap;
29 import android.os.Build;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.telephony.AnomalyReporter;
33 import android.telephony.SmsMessage;
34 import android.text.TextUtils;
35 
36 import com.android.internal.telephony.GsmAlphabet;
37 import com.android.internal.telephony.uicc.IccFileHandler;
38 
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.UUID;
42 import java.util.Locale;
43 /**
44  * Factory class, used for decoding raw byte arrays, received from baseband,
45  * into a CommandParams object.
46  * @hide
47  */
48 public class CommandParamsFactory extends Handler {
49     private static CommandParamsFactory sInstance = null;
50     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
51     private IconLoader mIconLoader;
52     private CommandParams mCmdParams = null;
53     private int mIconLoadState = LOAD_NO_ICON;
54     private RilMessageDecoder mCaller = null;
55     private boolean mloadIcon = false;
56     private String mSavedLanguage;
57     private String mRequestedLanguage;
58     private boolean mNoAlphaUsrCnf = false;
59     private boolean mStkSmsSendViaTelephony = false;
60 
61     // constants
62     static final int MSG_ID_LOAD_ICON_DONE = 1;
63 
64     // loading icons state parameters.
65     static final int LOAD_NO_ICON           = 0;
66     static final int LOAD_SINGLE_ICON       = 1;
67     static final int LOAD_MULTI_ICONS       = 2;
68 
69     // Command Qualifier values for PLI command
70     static final int DTTZ_SETTING                           = 0x03;
71     static final int LANGUAGE_SETTING                       = 0x04;
72 
73     // Command Qualifier value for language notification command
74     static final int NON_SPECIFIC_LANGUAGE                  = 0x00;
75     static final int SPECIFIC_LANGUAGE                      = 0x01;
76 
77     // As per TS 102.223 Annex C, Structure of CAT communications,
78     // the APDU length can be max 255 bytes. This leaves only 239 bytes for user
79     // input string. CMD details TLV + Device IDs TLV + Result TLV + Other
80     // details of TextString TLV not including user input take 16 bytes.
81     //
82     // If UCS2 encoding is used, maximum 118 UCS2 chars can be encoded in 238 bytes.
83     // Each UCS2 char takes 2 bytes. Byte Order Mask(BOM), 0xFEFF takes 2 bytes.
84     //
85     // If GSM 7 bit default(use 8 bits to represent a 7 bit char) format is used,
86     // maximum 239 chars can be encoded in 239 bytes since each char takes 1 byte.
87     //
88     // No issues for GSM 7 bit packed format encoding.
89 
90     private static final int MAX_GSM7_DEFAULT_CHARS = 239;
91     private static final int MAX_UCS2_CHARS = 118;
92 
93     // To Report Anomaly
94     public static final UUID NPE_WHEN_CALLED_SEND_CMD_PARAMS_UUID =
95             UUID.fromString("c2b85688-516e-11ee-be56-0242ac120002");
96     public static final String NPE_WHEN_CALLED_SEND_CMD_PARAMS_ERROR_MSG =
97             "mCaller[RilMessageDecoder] is Null when called SendCmdParams";
98     /**
99      * Returns a singleton instance of CommandParamsFactory
100      * @param caller Class used for queuing raw ril messages, decoding them into
101      *               CommandParams objects and sending the result back to the CAT Service.
102      * @param fh IccFileHandler Object
103      * @param context The Context
104      * @return CommandParamsFactory instance
105      */
getInstance(RilMessageDecoder caller, IccFileHandler fh, Context context)106     public static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
107             IccFileHandler fh, Context context) {
108         if (sInstance != null) {
109             return sInstance;
110         }
111         if (fh != null) {
112             return new CommandParamsFactory(caller, fh, context);
113         }
114         return null;
115     }
116 
CommandParamsFactory(RilMessageDecoder caller, IccFileHandler fh, Context context)117     private CommandParamsFactory(RilMessageDecoder caller, IccFileHandler fh, Context context) {
118         mCaller = caller;
119         mIconLoader = IconLoader.getInstance(this, fh);
120         try {
121             mNoAlphaUsrCnf = context.getResources().getBoolean(
122                     com.android.internal.R.bool.config_stkNoAlphaUsrCnf);
123         } catch (NotFoundException e) {
124             mNoAlphaUsrCnf = false;
125         }
126         try {
127             mStkSmsSendViaTelephony = context.getResources().getBoolean(
128                     com.android.internal.R.bool.config_stk_sms_send_support);
129         } catch (NotFoundException e) {
130             mStkSmsSendViaTelephony = false;
131         }
132     }
133 
processCommandDetails(List<ComprehensionTlv> ctlvs)134     private CommandDetails processCommandDetails(List<ComprehensionTlv> ctlvs) {
135         CommandDetails cmdDet = null;
136 
137         if (ctlvs != null) {
138             // Search for the Command Details object.
139             ComprehensionTlv ctlvCmdDet = searchForTag(
140                     ComprehensionTlvTag.COMMAND_DETAILS, ctlvs);
141             if (ctlvCmdDet != null) {
142                 try {
143                     cmdDet = ValueParser.retrieveCommandDetails(ctlvCmdDet);
144                 } catch (ResultException e) {
145                     CatLog.d(this,
146                             "processCommandDetails: Failed to procees command details e=" + e);
147                 }
148             }
149         }
150         return cmdDet;
151     }
152 
make(BerTlv berTlv)153     void make(BerTlv berTlv) {
154         if (berTlv == null) {
155             return;
156         }
157         // reset global state parameters.
158         mCmdParams = null;
159         mIconLoadState = LOAD_NO_ICON;
160         // only proactive command messages are processed.
161         if (berTlv.getTag() != BerTlv.BER_PROACTIVE_COMMAND_TAG) {
162             sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD);
163             return;
164         }
165         boolean cmdPending = false;
166         List<ComprehensionTlv> ctlvs = berTlv.getComprehensionTlvs();
167         // process command dtails from the tlv list.
168         CommandDetails cmdDet = processCommandDetails(ctlvs);
169         if (cmdDet == null) {
170             sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD);
171             return;
172         }
173 
174         // extract command type enumeration from the raw value stored inside
175         // the Command Details object.
176         AppInterface.CommandType cmdType = AppInterface.CommandType
177                 .fromInt(cmdDet.typeOfCommand);
178         if (cmdType == null) {
179             // This PROACTIVE COMMAND is presently not handled. Hence set
180             // result code as BEYOND_TERMINAL_CAPABILITY in TR.
181             mCmdParams = new CommandParams(cmdDet);
182             sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY);
183             return;
184         }
185 
186         // proactive command length is incorrect.
187         if (!berTlv.isLengthValid()) {
188             mCmdParams = new CommandParams(cmdDet);
189             sendCmdParams(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
190             return;
191         }
192 
193         try {
194             switch (cmdType) {
195                 case SET_UP_MENU:
196                     cmdPending = processSelectItem(cmdDet, ctlvs);
197                     break;
198                 case SELECT_ITEM:
199                     cmdPending = processSelectItem(cmdDet, ctlvs);
200                     break;
201                 case DISPLAY_TEXT:
202                     cmdPending = processDisplayText(cmdDet, ctlvs);
203                     break;
204                 case SET_UP_IDLE_MODE_TEXT:
205                     cmdPending = processSetUpIdleModeText(cmdDet, ctlvs);
206                     break;
207                 case GET_INKEY:
208                     cmdPending = processGetInkey(cmdDet, ctlvs);
209                     break;
210                 case GET_INPUT:
211                     cmdPending = processGetInput(cmdDet, ctlvs);
212                     break;
213                 case SEND_SMS:
214                     if (mStkSmsSendViaTelephony) {
215                         cmdPending = processSMSEventNotify(cmdDet, ctlvs);
216                     } else {
217                         cmdPending = processEventNotify(cmdDet, ctlvs);
218                     }
219                     break;
220                 case SEND_DTMF:
221                 case REFRESH:
222                 case RUN_AT:
223                 case SEND_SS:
224                 case SEND_USSD:
225                     cmdPending = processEventNotify(cmdDet, ctlvs);
226                     break;
227                 case GET_CHANNEL_STATUS:
228                 case SET_UP_CALL:
229                     cmdPending = processSetupCall(cmdDet, ctlvs);
230                     break;
231                 case LAUNCH_BROWSER:
232                     cmdPending = processLaunchBrowser(cmdDet, ctlvs);
233                     break;
234                 case PLAY_TONE:
235                     cmdPending = processPlayTone(cmdDet, ctlvs);
236                     break;
237                 case SET_UP_EVENT_LIST:
238                     cmdPending = processSetUpEventList(cmdDet, ctlvs);
239                     break;
240                 case PROVIDE_LOCAL_INFORMATION:
241                     cmdPending = processProvideLocalInfo(cmdDet, ctlvs);
242                     break;
243                 case LANGUAGE_NOTIFICATION:
244                     cmdPending = processLanguageNotification(cmdDet, ctlvs);
245                     break;
246                 case OPEN_CHANNEL:
247                 case CLOSE_CHANNEL:
248                 case RECEIVE_DATA:
249                 case SEND_DATA:
250                     cmdPending = processBIPClient(cmdDet, ctlvs);
251                     break;
252                 default:
253                     // unsupported proactive commands
254                     mCmdParams = new CommandParams(cmdDet);
255                     sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY);
256                     return;
257             }
258         } catch (ResultException e) {
259             CatLog.d(this, "make: caught ResultException e=" + e);
260             mCmdParams = new CommandParams(cmdDet);
261             sendCmdParams(e.result());
262             return;
263         }
264         if (!cmdPending) {
265             sendCmdParams(ResultCode.OK);
266         }
267     }
268 
269     @Override
handleMessage(Message msg)270     public void handleMessage(Message msg) {
271         switch (msg.what) {
272             case MSG_ID_LOAD_ICON_DONE:
273                 if (mIconLoader != null) {
274                     sendCmdParams(setIcons(msg.obj));
275                 }
276                 break;
277         }
278     }
279 
setIcons(Object data)280     private ResultCode setIcons(Object data) {
281         Bitmap[] icons = null;
282         int iconIndex = 0;
283 
284         if (data == null) {
285             CatLog.d(this, "Optional Icon data is NULL");
286             mCmdParams.mLoadIconFailed = true;
287             mloadIcon = false;
288             /** In case of icon load fail consider the
289              ** received proactive command as valid (sending RESULT OK) as
290              ** The result code, 'PRFRMD_ICON_NOT_DISPLAYED' will be added in the
291              ** terminal response by CatService/StkAppService if needed based on
292              ** the value of mLoadIconFailed.
293              */
294             return ResultCode.OK;
295         }
296         switch(mIconLoadState) {
297             case LOAD_SINGLE_ICON:
298                 mCmdParams.setIcon((Bitmap) data);
299                 break;
300             case LOAD_MULTI_ICONS:
301                 icons = (Bitmap[]) data;
302                 // set each item icon.
303                 for (Bitmap icon : icons) {
304                     mCmdParams.setIcon(icon);
305                     if (icon == null && mloadIcon) {
306                         CatLog.d(this, "Optional Icon data is NULL while loading multi icons");
307                         mCmdParams.mLoadIconFailed = true;
308                     }
309                 }
310                 break;
311         }
312         return ResultCode.OK;
313     }
314 
sendCmdParams(ResultCode resCode)315     private void sendCmdParams(ResultCode resCode) {
316         if (mCaller != null) {
317             mCaller.sendMsgParamsDecoded(resCode, mCmdParams);
318         } else {
319             CatLog.e(this, "mCaller[RilMessageDecoder] is NULL");
320             AnomalyReporter.reportAnomaly(NPE_WHEN_CALLED_SEND_CMD_PARAMS_UUID,
321                     NPE_WHEN_CALLED_SEND_CMD_PARAMS_ERROR_MSG);
322         }
323     }
324 
325     /**
326      * Search for a COMPREHENSION-TLV object with the given tag from a list
327      *
328      * @param tag A tag to search for
329      * @param ctlvs List of ComprehensionTlv objects used to search in
330      *
331      * @return A ComprehensionTlv object that has the tag value of {@code tag}.
332      *         If no object is found with the tag, null is returned.
333      */
334     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
searchForTag(ComprehensionTlvTag tag, List<ComprehensionTlv> ctlvs)335     private ComprehensionTlv searchForTag(ComprehensionTlvTag tag,
336             List<ComprehensionTlv> ctlvs) {
337         Iterator<ComprehensionTlv> iter = ctlvs.iterator();
338         return searchForNextTag(tag, iter);
339     }
340 
341     /**
342      * Search for the next COMPREHENSION-TLV object with the given tag from a
343      * list iterated by {@code iter}. {@code iter} points to the object next to
344      * the found object when this method returns. Used for searching the same
345      * list for similar tags, usually item id.
346      *
347      * @param tag A tag to search for
348      * @param iter Iterator for ComprehensionTlv objects used for search
349      *
350      * @return A ComprehensionTlv object that has the tag value of {@code tag}.
351      *         If no object is found with the tag, null is returned.
352      */
353     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
searchForNextTag(ComprehensionTlvTag tag, Iterator<ComprehensionTlv> iter)354     private ComprehensionTlv searchForNextTag(ComprehensionTlvTag tag,
355             Iterator<ComprehensionTlv> iter) {
356         int tagValue = tag.value();
357         while (iter.hasNext()) {
358             ComprehensionTlv ctlv = iter.next();
359             if (ctlv.getTag() == tagValue) {
360                 return ctlv;
361             }
362         }
363         return null;
364     }
365 
366     /**
367      * Processes DISPLAY_TEXT proactive command from the SIM card.
368      *
369      * @param cmdDet Command Details container object.
370      * @param ctlvs List of ComprehensionTlv objects following Command Details
371      *        object and Device Identities object within the proactive command
372      * @return true if the command is processing is pending and additional
373      *         asynchronous processing is required.
374      * @throws ResultException
375      */
processDisplayText(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)376     private boolean processDisplayText(CommandDetails cmdDet,
377             List<ComprehensionTlv> ctlvs)
378             throws ResultException {
379 
380         CatLog.d(this, "process DisplayText");
381 
382         TextMessage textMsg = new TextMessage();
383         IconId iconId = null;
384 
385         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING,
386                 ctlvs);
387         if (ctlv != null) {
388             textMsg.text = ValueParser.retrieveTextString(ctlv);
389         }
390         // If the tlv object doesn't exist or the it is a null object reply
391         // with command not understood.
392         if (textMsg.text == null) {
393             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
394         }
395 
396         ctlv = searchForTag(ComprehensionTlvTag.IMMEDIATE_RESPONSE, ctlvs);
397         if (ctlv != null) {
398             textMsg.responseNeeded = false;
399         }
400         // parse icon identifier
401         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
402         if (ctlv != null) {
403             iconId = ValueParser.retrieveIconId(ctlv);
404             textMsg.iconSelfExplanatory = iconId.selfExplanatory;
405         }
406         // parse tone duration
407         ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
408         if (ctlv != null) {
409             textMsg.duration = ValueParser.retrieveDuration(ctlv);
410         }
411 
412         // Parse command qualifier parameters.
413         textMsg.isHighPriority = (cmdDet.commandQualifier & 0x01) != 0;
414         textMsg.userClear = (cmdDet.commandQualifier & 0x80) != 0;
415 
416         mCmdParams = new DisplayTextParams(cmdDet, textMsg);
417 
418         if (iconId != null) {
419             mloadIcon = true;
420             mIconLoadState = LOAD_SINGLE_ICON;
421             mIconLoader.loadIcon(iconId.recordNumber, this
422                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
423             return true;
424         }
425         return false;
426     }
427 
428     /**
429      * Processes SET_UP_IDLE_MODE_TEXT proactive command from the SIM card.
430      *
431      * @param cmdDet Command Details container object.
432      * @param ctlvs List of ComprehensionTlv objects following Command Details
433      *        object and Device Identities object within the proactive command
434      * @return true if the command is processing is pending and additional
435      *         asynchronous processing is required.
436      * @throws ResultException
437      */
processSetUpIdleModeText(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)438     private boolean processSetUpIdleModeText(CommandDetails cmdDet,
439             List<ComprehensionTlv> ctlvs) throws ResultException {
440 
441         CatLog.d(this, "process SetUpIdleModeText");
442 
443         TextMessage textMsg = new TextMessage();
444         IconId iconId = null;
445 
446         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING,
447                 ctlvs);
448         if (ctlv != null) {
449             textMsg.text = ValueParser.retrieveTextString(ctlv);
450         }
451 
452         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
453         if (ctlv != null) {
454             iconId = ValueParser.retrieveIconId(ctlv);
455             textMsg.iconSelfExplanatory = iconId.selfExplanatory;
456         }
457 
458         /*
459          * If the tlv object doesn't contain text and the icon is not self
460          * explanatory then reply with command not understood.
461          */
462 
463         if (textMsg.text == null && iconId != null && !textMsg.iconSelfExplanatory) {
464             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
465         }
466         mCmdParams = new DisplayTextParams(cmdDet, textMsg);
467 
468         if (iconId != null) {
469             mloadIcon = true;
470             mIconLoadState = LOAD_SINGLE_ICON;
471             mIconLoader.loadIcon(iconId.recordNumber, this
472                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
473             return true;
474         }
475         return false;
476     }
477 
478     /**
479      * Processes GET_INKEY proactive command from the SIM card.
480      *
481      * @param cmdDet Command Details container object.
482      * @param ctlvs List of ComprehensionTlv objects following Command Details
483      *        object and Device Identities object within the proactive command
484      * @return true if the command is processing is pending and additional
485      *         asynchronous processing is required.
486      * @throws ResultException
487      */
processGetInkey(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)488     private boolean processGetInkey(CommandDetails cmdDet,
489             List<ComprehensionTlv> ctlvs) throws ResultException {
490 
491         CatLog.d(this, "process GetInkey");
492 
493         Input input = new Input();
494         IconId iconId = null;
495 
496         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING,
497                 ctlvs);
498         if (ctlv != null) {
499             input.text = ValueParser.retrieveTextString(ctlv);
500         } else {
501             throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
502         }
503         // parse icon identifier
504         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
505         if (ctlv != null) {
506             iconId = ValueParser.retrieveIconId(ctlv);
507             input.iconSelfExplanatory = iconId.selfExplanatory;
508         }
509 
510         // parse duration
511         ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
512         if (ctlv != null) {
513             input.duration = ValueParser.retrieveDuration(ctlv);
514         }
515 
516         input.minLen = 1;
517         input.maxLen = 1;
518 
519         input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0;
520         input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0;
521         input.yesNo = (cmdDet.commandQualifier & 0x04) != 0;
522         input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0;
523         input.echo = true;
524 
525         mCmdParams = new GetInputParams(cmdDet, input);
526 
527         if (iconId != null) {
528             mloadIcon = true;
529             mIconLoadState = LOAD_SINGLE_ICON;
530             mIconLoader.loadIcon(iconId.recordNumber, this
531                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
532             return true;
533         }
534         return false;
535     }
536 
537     /**
538      * Processes GET_INPUT proactive command from the SIM card.
539      *
540      * @param cmdDet Command Details container object.
541      * @param ctlvs List of ComprehensionTlv objects following Command Details
542      *        object and Device Identities object within the proactive command
543      * @return true if the command is processing is pending and additional
544      *         asynchronous processing is required.
545      * @throws ResultException
546      */
processGetInput(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)547     private boolean processGetInput(CommandDetails cmdDet,
548             List<ComprehensionTlv> ctlvs) throws ResultException {
549 
550         CatLog.d(this, "process GetInput");
551 
552         Input input = new Input();
553         IconId iconId = null;
554 
555         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING,
556                 ctlvs);
557         if (ctlv != null) {
558             input.text = ValueParser.retrieveTextString(ctlv);
559         } else {
560             throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
561         }
562 
563         ctlv = searchForTag(ComprehensionTlvTag.RESPONSE_LENGTH, ctlvs);
564         if (ctlv != null) {
565             try {
566                 byte[] rawValue = ctlv.getRawValue();
567                 int valueIndex = ctlv.getValueIndex();
568                 input.minLen = rawValue[valueIndex] & 0xff;
569                 input.maxLen = rawValue[valueIndex + 1] & 0xff;
570             } catch (IndexOutOfBoundsException e) {
571                 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
572             }
573         } else {
574             throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
575         }
576 
577         ctlv = searchForTag(ComprehensionTlvTag.DEFAULT_TEXT, ctlvs);
578         if (ctlv != null) {
579             input.defaultText = ValueParser.retrieveTextString(ctlv);
580         }
581         // parse icon identifier
582         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
583         if (ctlv != null) {
584             iconId = ValueParser.retrieveIconId(ctlv);
585             input.iconSelfExplanatory = iconId.selfExplanatory;
586         }
587 
588         ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
589         if (ctlv != null) {
590             input.duration = ValueParser.retrieveDuration(ctlv);
591         }
592 
593         input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0;
594         input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0;
595         input.echo = (cmdDet.commandQualifier & 0x04) == 0;
596         input.packed = (cmdDet.commandQualifier & 0x08) != 0;
597         input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0;
598 
599         // Truncate the maxLen if it exceeds the max number of chars that can
600         // be encoded. Limit depends on DCS in Command Qualifier.
601         if (input.ucs2 && input.maxLen > MAX_UCS2_CHARS) {
602             CatLog.d(this, "UCS2: received maxLen = " + input.maxLen +
603                     ", truncating to " + MAX_UCS2_CHARS);
604             input.maxLen = MAX_UCS2_CHARS;
605         } else if (!input.packed && input.maxLen > MAX_GSM7_DEFAULT_CHARS) {
606             CatLog.d(this, "GSM 7Bit Default: received maxLen = " + input.maxLen +
607                     ", truncating to " + MAX_GSM7_DEFAULT_CHARS);
608             input.maxLen = MAX_GSM7_DEFAULT_CHARS;
609         }
610 
611         mCmdParams = new GetInputParams(cmdDet, input);
612 
613         if (iconId != null) {
614             mloadIcon = true;
615             mIconLoadState = LOAD_SINGLE_ICON;
616             mIconLoader.loadIcon(iconId.recordNumber, this
617                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
618             return true;
619         }
620         return false;
621     }
622 
623     /**
624      * Processes SELECT_ITEM proactive command from the SIM card.
625      *
626      * @param cmdDet Command Details container object.
627      * @param ctlvs List of ComprehensionTlv objects following Command Details
628      *        object and Device Identities object within the proactive command
629      * @return true if the command is processing is pending and additional
630      *         asynchronous processing is required.
631      * @throws ResultException
632      */
processSelectItem(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)633     private boolean processSelectItem(CommandDetails cmdDet,
634             List<ComprehensionTlv> ctlvs) throws ResultException {
635 
636         CatLog.d(this, "process SelectItem");
637 
638         Menu menu = new Menu();
639         IconId titleIconId = null;
640         ItemsIconId itemsIconId = null;
641         Iterator<ComprehensionTlv> iter = ctlvs.iterator();
642 
643         AppInterface.CommandType cmdType = AppInterface.CommandType
644                 .fromInt(cmdDet.typeOfCommand);
645 
646         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID,
647                 ctlvs);
648         if (ctlv != null) {
649             menu.title = ValueParser.retrieveAlphaId(ctlv, mNoAlphaUsrCnf);
650         } else if (cmdType == AppInterface.CommandType.SET_UP_MENU) {
651             // According to spec ETSI TS 102 223 section 6.10.3, the
652             // Alpha ID is mandatory (and also part of minimum set of
653             // elements required) for SET_UP_MENU. If it is not received
654             // by ME, then ME should respond with "error: missing minimum
655             // information" and not "command performed successfully".
656             throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
657         }
658 
659         while (true) {
660             ctlv = searchForNextTag(ComprehensionTlvTag.ITEM, iter);
661             if (ctlv != null) {
662                 menu.items.add(ValueParser.retrieveItem(ctlv));
663             } else {
664                 break;
665             }
666         }
667 
668         // We must have at least one menu item.
669         if (menu.items.size() == 0) {
670             throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
671         }
672 
673         ctlv = searchForTag(ComprehensionTlvTag.ITEM_ID, ctlvs);
674         if (ctlv != null) {
675             // CAT items are listed 1...n while list start at 0, need to
676             // subtract one.
677             menu.defaultItem = ValueParser.retrieveItemId(ctlv) - 1;
678         }
679 
680         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
681         if (ctlv != null) {
682             mIconLoadState = LOAD_SINGLE_ICON;
683             titleIconId = ValueParser.retrieveIconId(ctlv);
684             menu.titleIconSelfExplanatory = titleIconId.selfExplanatory;
685         }
686 
687         ctlv = searchForTag(ComprehensionTlvTag.ITEM_ICON_ID_LIST, ctlvs);
688         if (ctlv != null) {
689             mIconLoadState = LOAD_MULTI_ICONS;
690             itemsIconId = ValueParser.retrieveItemsIconId(ctlv);
691             menu.itemsIconSelfExplanatory = itemsIconId.selfExplanatory;
692         }
693 
694         boolean presentTypeSpecified = (cmdDet.commandQualifier & 0x01) != 0;
695         if (presentTypeSpecified) {
696             if ((cmdDet.commandQualifier & 0x02) == 0) {
697                 menu.presentationType = PresentationType.DATA_VALUES;
698             } else {
699                 menu.presentationType = PresentationType.NAVIGATION_OPTIONS;
700             }
701         }
702         menu.softKeyPreferred = (cmdDet.commandQualifier & 0x04) != 0;
703         menu.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0;
704 
705         mCmdParams = new SelectItemParams(cmdDet, menu, titleIconId != null);
706 
707         // Load icons data if needed.
708         switch(mIconLoadState) {
709             case LOAD_NO_ICON:
710                 return false;
711             case LOAD_SINGLE_ICON:
712                 mloadIcon = true;
713                 mIconLoader.loadIcon(titleIconId.recordNumber, this
714                         .obtainMessage(MSG_ID_LOAD_ICON_DONE));
715                 break;
716             case LOAD_MULTI_ICONS:
717                 int[] recordNumbers = itemsIconId.recordNumbers;
718                 if (titleIconId != null) {
719                     // Create a new array for all the icons (title and items).
720                     recordNumbers = new int[itemsIconId.recordNumbers.length + 1];
721                     recordNumbers[0] = titleIconId.recordNumber;
722                     System.arraycopy(itemsIconId.recordNumbers, 0, recordNumbers,
723                             1, itemsIconId.recordNumbers.length);
724                 }
725                 mloadIcon = true;
726                 mIconLoader.loadIcons(recordNumbers, this
727                         .obtainMessage(MSG_ID_LOAD_ICON_DONE));
728                 break;
729         }
730         return true;
731     }
732 
733     /**
734      * Processes EVENT_NOTIFY message from baseband.
735      *
736      * @param cmdDet Command Details container object.
737      * @param ctlvs List of ComprehensionTlv objects following Command Details
738      *        object and Device Identities object within the proactive command
739      * @return true if the command is processing is pending and additional
740      *         asynchronous processing is required.
741      */
processEventNotify(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)742     private boolean processEventNotify(CommandDetails cmdDet,
743             List<ComprehensionTlv> ctlvs) throws ResultException {
744 
745         CatLog.d(this, "process EventNotify");
746 
747         TextMessage textMsg = new TextMessage();
748         IconId iconId = null;
749 
750         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID,
751                 ctlvs);
752         textMsg.text = ValueParser.retrieveAlphaId(ctlv, mNoAlphaUsrCnf);
753 
754         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
755         if (ctlv != null) {
756             iconId = ValueParser.retrieveIconId(ctlv);
757             textMsg.iconSelfExplanatory = iconId.selfExplanatory;
758         }
759 
760         textMsg.responseNeeded = false;
761         mCmdParams = new DisplayTextParams(cmdDet, textMsg);
762 
763         if (iconId != null) {
764             mloadIcon = true;
765             mIconLoadState = LOAD_SINGLE_ICON;
766             mIconLoader.loadIcon(iconId.recordNumber, this
767                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
768             return true;
769         }
770         return false;
771     }
772 
773 
774     /**
775      * Processes SMS_EVENT_NOTIFY message from baseband.
776      *
777      * Method extracts values such as Alpha Id,Icon Id,Sms Tpdu etc from the ComprehensionTlv,
778      * in order to create the CommandParams i.e. SendSMSParams.
779      *
780      * @param cmdDet Command Details container object.
781      * @param ctlvs List of ComprehensionTlv objects following Command Details
782      *        object and Device Identities object within the proactive command
783      * @return true if the command is processing is pending and additional
784      *         asynchronous processing is required.
785      * @hide
786      */
processSMSEventNotify(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)787     public boolean processSMSEventNotify(CommandDetails cmdDet,
788             List<ComprehensionTlv> ctlvs) throws ResultException {
789         CatLog.d(this, "processSMSEventNotify");
790 
791         TextMessage textMsg = new TextMessage();
792         IconId iconId = null;
793 
794         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID,
795                 ctlvs);
796         /* Retrieves alpha identifier from an Alpha Identifier COMPREHENSION-TLV object.
797          *
798          * String corresponding to the alpha identifier is obtained and saved as part of
799          * the DisplayTextParams.
800          */
801         textMsg.text = ValueParser.retrieveAlphaId(ctlv, mNoAlphaUsrCnf);
802 
803         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
804         if (ctlv != null) {
805             // Retrieves icon id from the Icon Identifier COMPREHENSION-TLV object
806             iconId = ValueParser.retrieveIconId(ctlv);
807             textMsg.iconSelfExplanatory = iconId.selfExplanatory;
808         }
809 
810         textMsg.responseNeeded = false;
811         DisplayTextParams displayTextParams = new DisplayTextParams(cmdDet, textMsg);
812         ComprehensionTlv ctlvTpdu = searchForTag(ComprehensionTlvTag.SMS_TPDU,
813                 ctlvs);
814         // Retrieves smsMessage from the SMS TPDU COMPREHENSION-TLV object
815         SmsMessage smsMessage = ValueParser.retrieveTpduAsSmsMessage(ctlvTpdu);
816         if (smsMessage != null) {
817             TextMessage smsText = new TextMessage();
818             // Obtains the sms message content.
819             smsText.text = smsMessage.getMessageBody();
820             TextMessage destAddr = new TextMessage();
821             // Obtains the destination Address.
822             destAddr.text = smsMessage.getRecipientAddress();
823             mCmdParams = new SendSMSParams(cmdDet, smsText, destAddr, displayTextParams);
824             return false;
825         }
826         return true;
827     }
828 
829     /**
830      * Processes SET_UP_EVENT_LIST proactive command from the SIM card.
831      *
832      * @param cmdDet Command Details object retrieved.
833      * @param ctlvs List of ComprehensionTlv objects following Command Details
834      *        object and Device Identities object within the proactive command
835      * @return false. This function always returns false meaning that the command
836      *         processing is  not pending and additional asynchronous processing
837      *         is not required.
838      */
processSetUpEventList(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)839     private boolean processSetUpEventList(CommandDetails cmdDet,
840             List<ComprehensionTlv> ctlvs) {
841 
842         CatLog.d(this, "process SetUpEventList");
843         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.EVENT_LIST, ctlvs);
844         if (ctlv != null) {
845             try {
846                 byte[] rawValue = ctlv.getRawValue();
847                 int valueIndex = ctlv.getValueIndex();
848                 int valueLen = ctlv.getLength();
849                 int[] eventList = new int[valueLen];
850                 int eventValue = -1;
851                 int i = 0;
852                 while (valueLen > 0) {
853                     eventValue = rawValue[valueIndex] & 0xff;
854                     valueIndex++;
855                     valueLen--;
856 
857                     switch (eventValue) {
858                         case USER_ACTIVITY_EVENT:
859                         case IDLE_SCREEN_AVAILABLE_EVENT:
860                         case LANGUAGE_SELECTION_EVENT:
861                         case BROWSER_TERMINATION_EVENT:
862                         case BROWSING_STATUS_EVENT:
863                             eventList[i] = eventValue;
864                             i++;
865                             break;
866                         default:
867                             break;
868                     }
869 
870                 }
871                 mCmdParams = new SetEventListParams(cmdDet, eventList);
872             } catch (IndexOutOfBoundsException e) {
873                 CatLog.e(this, " IndexOutofBoundException in processSetUpEventList");
874             }
875         }
876         return false;
877     }
878 
879     /**
880      * Processes LAUNCH_BROWSER proactive command from the SIM card.
881      *
882      * @param cmdDet Command Details container object.
883      * @param ctlvs List of ComprehensionTlv objects following Command Details
884      *        object and Device Identities object within the proactive command
885      * @return true if the command is processing is pending and additional
886      *         asynchronous processing is required.
887      * @throws ResultException
888      */
processLaunchBrowser(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)889     private boolean processLaunchBrowser(CommandDetails cmdDet,
890             List<ComprehensionTlv> ctlvs) throws ResultException {
891 
892         CatLog.d(this, "process LaunchBrowser");
893 
894         TextMessage confirmMsg = new TextMessage();
895         IconId iconId = null;
896         String url = null;
897 
898         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.URL, ctlvs);
899         if (ctlv != null) {
900             try {
901                 byte[] rawValue = ctlv.getRawValue();
902                 int valueIndex = ctlv.getValueIndex();
903                 int valueLen = ctlv.getLength();
904                 if (valueLen > 0) {
905                     url = GsmAlphabet.gsm8BitUnpackedToString(rawValue,
906                             valueIndex, valueLen);
907                 } else {
908                     url = null;
909                 }
910             } catch (IndexOutOfBoundsException e) {
911                 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
912             }
913         }
914 
915         // parse alpha identifier.
916         ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs);
917         confirmMsg.text = ValueParser.retrieveAlphaId(ctlv, mNoAlphaUsrCnf);
918 
919         // parse icon identifier
920         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
921         if (ctlv != null) {
922             iconId = ValueParser.retrieveIconId(ctlv);
923             confirmMsg.iconSelfExplanatory = iconId.selfExplanatory;
924         }
925 
926         // parse command qualifier value.
927         LaunchBrowserMode mode;
928         switch (cmdDet.commandQualifier) {
929             case 0x00:
930             default:
931                 mode = LaunchBrowserMode.LAUNCH_IF_NOT_ALREADY_LAUNCHED;
932                 break;
933             case 0x02:
934                 mode = LaunchBrowserMode.USE_EXISTING_BROWSER;
935                 break;
936             case 0x03:
937                 mode = LaunchBrowserMode.LAUNCH_NEW_BROWSER;
938                 break;
939         }
940 
941         mCmdParams = new LaunchBrowserParams(cmdDet, confirmMsg, url, mode);
942 
943         if (iconId != null) {
944             mIconLoadState = LOAD_SINGLE_ICON;
945             mIconLoader.loadIcon(iconId.recordNumber, this
946                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
947             return true;
948         }
949         return false;
950     }
951 
952     /**
953      * Processes PLAY_TONE proactive command from the SIM card.
954      *
955      * @param cmdDet Command Details container object.
956      * @param ctlvs List of ComprehensionTlv objects following Command Details
957      *        object and Device Identities object within the proactive command
958      * @return true if the command is processing is pending and additional
959      *         asynchronous processing is required.t
960      * @throws ResultException
961      */
processPlayTone(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)962     private boolean processPlayTone(CommandDetails cmdDet,
963             List<ComprehensionTlv> ctlvs) throws ResultException {
964 
965         CatLog.d(this, "process PlayTone");
966 
967         Tone tone = null;
968         TextMessage textMsg = new TextMessage();
969         Duration duration = null;
970         IconId iconId = null;
971 
972         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TONE, ctlvs);
973         if (ctlv != null) {
974             // Nothing to do for null objects.
975             if (ctlv.getLength() > 0) {
976                 try {
977                     byte[] rawValue = ctlv.getRawValue();
978                     int valueIndex = ctlv.getValueIndex();
979                     int toneVal = rawValue[valueIndex];
980                     tone = Tone.fromInt(toneVal);
981                 } catch (IndexOutOfBoundsException e) {
982                     throw new ResultException(
983                             ResultCode.CMD_DATA_NOT_UNDERSTOOD);
984                 }
985             }
986         }
987         // parse alpha identifier
988         ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs);
989         if (ctlv != null) {
990             textMsg.text = ValueParser.retrieveAlphaId(ctlv, mNoAlphaUsrCnf);
991             // Assign the tone message text to empty string, if alpha identifier
992             // data is null. If no alpha identifier tlv is present, then tone
993             // message text will be null.
994             if (textMsg.text == null) textMsg.text = "";
995         }
996         // parse tone duration
997         ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
998         if (ctlv != null) {
999             duration = ValueParser.retrieveDuration(ctlv);
1000         }
1001         // parse icon identifier
1002         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
1003         if (ctlv != null) {
1004             iconId = ValueParser.retrieveIconId(ctlv);
1005             textMsg.iconSelfExplanatory = iconId.selfExplanatory;
1006         }
1007 
1008         boolean vibrate = (cmdDet.commandQualifier & 0x01) != 0x00;
1009 
1010         textMsg.responseNeeded = false;
1011         mCmdParams = new PlayToneParams(cmdDet, textMsg, tone, duration, vibrate);
1012 
1013         if (iconId != null) {
1014             mIconLoadState = LOAD_SINGLE_ICON;
1015             mIconLoader.loadIcon(iconId.recordNumber, this
1016                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
1017             return true;
1018         }
1019         return false;
1020     }
1021 
1022     /**
1023      * Processes SETUP_CALL proactive command from the SIM card.
1024      *
1025      * @param cmdDet Command Details object retrieved from the proactive command
1026      *        object
1027      * @param ctlvs List of ComprehensionTlv objects following Command Details
1028      *        object and Device Identities object within the proactive command
1029      * @return true if the command is processing is pending and additional
1030      *         asynchronous processing is required.
1031      */
processSetupCall(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)1032     private boolean processSetupCall(CommandDetails cmdDet,
1033             List<ComprehensionTlv> ctlvs) throws ResultException {
1034         CatLog.d(this, "process SetupCall");
1035 
1036         Iterator<ComprehensionTlv> iter = ctlvs.iterator();
1037         ComprehensionTlv ctlv = null;
1038         // User confirmation phase message.
1039         TextMessage confirmMsg = new TextMessage();
1040         // Call set up phase message.
1041         TextMessage callMsg = new TextMessage();
1042         IconId confirmIconId = null;
1043         IconId callIconId = null;
1044 
1045         // get confirmation message string.
1046         ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter);
1047         confirmMsg.text = ValueParser.retrieveAlphaId(ctlv, mNoAlphaUsrCnf);
1048 
1049         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
1050         if (ctlv != null) {
1051             confirmIconId = ValueParser.retrieveIconId(ctlv);
1052             confirmMsg.iconSelfExplanatory = confirmIconId.selfExplanatory;
1053         }
1054 
1055         // get call set up message string.
1056         ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter);
1057         if (ctlv != null) {
1058             callMsg.text = ValueParser.retrieveAlphaId(ctlv, mNoAlphaUsrCnf);
1059         }
1060 
1061         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
1062         if (ctlv != null) {
1063             callIconId = ValueParser.retrieveIconId(ctlv);
1064             callMsg.iconSelfExplanatory = callIconId.selfExplanatory;
1065         }
1066 
1067         mCmdParams = new CallSetupParams(cmdDet, confirmMsg, callMsg);
1068 
1069         if (confirmIconId != null || callIconId != null) {
1070             mIconLoadState = LOAD_MULTI_ICONS;
1071             int[] recordNumbers = new int[2];
1072             recordNumbers[0] = confirmIconId != null
1073                     ? confirmIconId.recordNumber : -1;
1074             recordNumbers[1] = callIconId != null ? callIconId.recordNumber
1075                     : -1;
1076 
1077             mIconLoader.loadIcons(recordNumbers, this
1078                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
1079             return true;
1080         }
1081         return false;
1082     }
1083 
processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)1084     private boolean processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)
1085             throws ResultException {
1086         CatLog.d(this, "process ProvideLocalInfo");
1087         switch (cmdDet.commandQualifier) {
1088             case DTTZ_SETTING:
1089                 CatLog.d(this, "PLI [DTTZ_SETTING]");
1090                 mCmdParams = new CommandParams(cmdDet);
1091                 break;
1092             case LANGUAGE_SETTING:
1093                 CatLog.d(this, "PLI [LANGUAGE_SETTING]");
1094                 mCmdParams = new CommandParams(cmdDet);
1095                 break;
1096             default:
1097                 CatLog.d(this, "PLI[" + cmdDet.commandQualifier + "] Command Not Supported");
1098                 mCmdParams = new CommandParams(cmdDet);
1099                 throw new ResultException(ResultCode.BEYOND_TERMINAL_CAPABILITY);
1100         }
1101         return false;
1102     }
1103 
1104     /**
1105      * Processes LANGUAGE_NOTIFICATION proactive command from the SIM card.
1106      *
1107      * The SPECIFIC_LANGUAGE notification sets the specified language.
1108      * The NON_SPECIFIC_LANGUAGE notification restores the last specifically set language.
1109      *
1110      * @param cmdDet Command Details object retrieved from the proactive command object
1111      * @param ctlvs List of ComprehensionTlv objects following Command Details
1112      *        object and Device Identities object within the proactive command
1113      * @return false. This function always returns false meaning that the command
1114      *         processing is  not pending and additional asynchronous processing
1115      *         is not required.
1116      */
processLanguageNotification(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)1117     private boolean processLanguageNotification(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)
1118             throws ResultException {
1119         CatLog.d(this, "process Language Notification");
1120 
1121         String desiredLanguage = null;
1122         String currentLanguage = Locale.getDefault().getLanguage();
1123         switch (cmdDet.commandQualifier) {
1124             case NON_SPECIFIC_LANGUAGE:
1125                 if (!TextUtils.isEmpty(mSavedLanguage) && (!TextUtils.isEmpty(mRequestedLanguage)
1126                         && mRequestedLanguage.equals(currentLanguage))) {
1127                     CatLog.d(this, "Non-specific language notification changes the language "
1128                             + "setting back to " + mSavedLanguage);
1129                     desiredLanguage = mSavedLanguage;
1130                 }
1131 
1132                 mSavedLanguage = null;
1133                 mRequestedLanguage = null;
1134                 break;
1135             case SPECIFIC_LANGUAGE:
1136                 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.LANGUAGE, ctlvs);
1137                 if (ctlv != null) {
1138                     int valueLen = ctlv.getLength();
1139                     if (valueLen != 2) {
1140                         throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
1141                     }
1142 
1143                     byte[] rawValue = ctlv.getRawValue();
1144                     int valueIndex = ctlv.getValueIndex();
1145                     desiredLanguage = GsmAlphabet.gsm8BitUnpackedToString(rawValue, valueIndex, 2);
1146 
1147                     if (TextUtils.isEmpty(mSavedLanguage) || (!TextUtils.isEmpty(mRequestedLanguage)
1148                             && !mRequestedLanguage.equals(currentLanguage))) {
1149                         mSavedLanguage = currentLanguage;
1150                     }
1151                     mRequestedLanguage = desiredLanguage;
1152                     CatLog.d(this, "Specific language notification changes the language setting to "
1153                             + mRequestedLanguage);
1154                 }
1155                 break;
1156             default:
1157                 CatLog.d(this, "LN[" + cmdDet.commandQualifier + "] Command Not Supported");
1158                 break;
1159         }
1160 
1161         mCmdParams = new LanguageParams(cmdDet, desiredLanguage);
1162         return false;
1163     }
1164 
processBIPClient(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)1165     private boolean processBIPClient(CommandDetails cmdDet,
1166             List<ComprehensionTlv> ctlvs) throws ResultException {
1167         AppInterface.CommandType commandType =
1168                 AppInterface.CommandType.fromInt(cmdDet.typeOfCommand);
1169         if (commandType != null) {
1170             CatLog.d(this, "process "+ commandType.name());
1171         }
1172 
1173         TextMessage textMsg = new TextMessage();
1174         IconId iconId = null;
1175         ComprehensionTlv ctlv = null;
1176         boolean has_alpha_id = false;
1177 
1178         // parse alpha identifier
1179         ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs);
1180         if (ctlv != null) {
1181             textMsg.text = ValueParser.retrieveAlphaId(ctlv, mNoAlphaUsrCnf);
1182             CatLog.d(this, "alpha TLV text=" + textMsg.text);
1183             has_alpha_id = true;
1184         }
1185 
1186         // parse icon identifier
1187         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
1188         if (ctlv != null) {
1189             iconId = ValueParser.retrieveIconId(ctlv);
1190             textMsg.iconSelfExplanatory = iconId.selfExplanatory;
1191         }
1192 
1193         textMsg.responseNeeded = false;
1194         mCmdParams = new BIPClientParams(cmdDet, textMsg, has_alpha_id);
1195 
1196         if (iconId != null) {
1197             mIconLoadState = LOAD_SINGLE_ICON;
1198             mIconLoader.loadIcon(iconId.recordNumber, obtainMessage(MSG_ID_LOAD_ICON_DONE));
1199             return true;
1200         }
1201         return false;
1202     }
1203 
1204     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
dispose()1205     public void dispose() {
1206         mIconLoader.dispose();
1207         mIconLoader = null;
1208         mCmdParams = null;
1209         mCaller = null;
1210         sInstance = null;
1211     }
1212 }
1213