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