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