• 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 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             sendCmdParams(setIcons(msg.obj));
234             break;
235         }
236     }
237 
setIcons(Object data)238     private ResultCode setIcons(Object data) {
239         Bitmap[] icons = null;
240         int iconIndex = 0;
241 
242         if (data == null) {
243             CatLog.d(this, "Optional Icon data is NULL");
244             mCmdParams.mLoadIconFailed = true;
245             mloadIcon = false;
246             /** In case of icon load fail consider the
247             ** received proactive command as valid (sending RESULT OK) as
248             ** The result code, 'PRFRMD_ICON_NOT_DISPLAYED' will be added in the
249             ** terminal response by CatService/StkAppService if needed based on
250             ** the value of mLoadIconFailed.
251             */
252             return ResultCode.OK;
253         }
254         switch(mIconLoadState) {
255         case LOAD_SINGLE_ICON:
256             mCmdParams.setIcon((Bitmap) data);
257             break;
258         case LOAD_MULTI_ICONS:
259             icons = (Bitmap[]) data;
260             // set each item icon.
261             for (Bitmap icon : icons) {
262                 mCmdParams.setIcon(icon);
263                 if (icon == null && mloadIcon) {
264                     CatLog.d(this, "Optional Icon data is NULL while loading multi icons");
265                     mCmdParams.mLoadIconFailed = true;
266                 }
267             }
268             break;
269         }
270         return ResultCode.OK;
271     }
272 
sendCmdParams(ResultCode resCode)273     private void sendCmdParams(ResultCode resCode) {
274         mCaller.sendMsgParamsDecoded(resCode, mCmdParams);
275     }
276 
277     /**
278      * Search for a COMPREHENSION-TLV object with the given tag from a list
279      *
280      * @param tag A tag to search for
281      * @param ctlvs List of ComprehensionTlv objects used to search in
282      *
283      * @return A ComprehensionTlv object that has the tag value of {@code tag}.
284      *         If no object is found with the tag, null is returned.
285      */
searchForTag(ComprehensionTlvTag tag, List<ComprehensionTlv> ctlvs)286     private ComprehensionTlv searchForTag(ComprehensionTlvTag tag,
287             List<ComprehensionTlv> ctlvs) {
288         Iterator<ComprehensionTlv> iter = ctlvs.iterator();
289         return searchForNextTag(tag, iter);
290     }
291 
292     /**
293      * Search for the next COMPREHENSION-TLV object with the given tag from a
294      * list iterated by {@code iter}. {@code iter} points to the object next to
295      * the found object when this method returns. Used for searching the same
296      * list for similar tags, usually item id.
297      *
298      * @param tag A tag to search for
299      * @param iter Iterator for ComprehensionTlv objects used for search
300      *
301      * @return A ComprehensionTlv object that has the tag value of {@code tag}.
302      *         If no object is found with the tag, null is returned.
303      */
searchForNextTag(ComprehensionTlvTag tag, Iterator<ComprehensionTlv> iter)304     private ComprehensionTlv searchForNextTag(ComprehensionTlvTag tag,
305             Iterator<ComprehensionTlv> iter) {
306         int tagValue = tag.value();
307         while (iter.hasNext()) {
308             ComprehensionTlv ctlv = iter.next();
309             if (ctlv.getTag() == tagValue) {
310                 return ctlv;
311             }
312         }
313         return null;
314     }
315 
316     /**
317      * Processes DISPLAY_TEXT proactive command from the SIM card.
318      *
319      * @param cmdDet Command Details container object.
320      * @param ctlvs List of ComprehensionTlv objects following Command Details
321      *        object and Device Identities object within the proactive command
322      * @return true if the command is processing is pending and additional
323      *         asynchronous processing is required.
324      * @throws ResultException
325      */
processDisplayText(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)326     private boolean processDisplayText(CommandDetails cmdDet,
327             List<ComprehensionTlv> ctlvs)
328             throws ResultException {
329 
330         CatLog.d(this, "process DisplayText");
331 
332         TextMessage textMsg = new TextMessage();
333         IconId iconId = null;
334 
335         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING,
336                 ctlvs);
337         if (ctlv != null) {
338             textMsg.text = ValueParser.retrieveTextString(ctlv);
339         }
340         // If the tlv object doesn't exist or the it is a null object reply
341         // with command not understood.
342         if (textMsg.text == null) {
343             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
344         }
345 
346         ctlv = searchForTag(ComprehensionTlvTag.IMMEDIATE_RESPONSE, ctlvs);
347         if (ctlv != null) {
348             textMsg.responseNeeded = false;
349         }
350         // parse icon identifier
351         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
352         if (ctlv != null) {
353             iconId = ValueParser.retrieveIconId(ctlv);
354             textMsg.iconSelfExplanatory = iconId.selfExplanatory;
355         }
356         // parse tone duration
357         ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
358         if (ctlv != null) {
359             textMsg.duration = ValueParser.retrieveDuration(ctlv);
360         }
361 
362         // Parse command qualifier parameters.
363         textMsg.isHighPriority = (cmdDet.commandQualifier & 0x01) != 0;
364         textMsg.userClear = (cmdDet.commandQualifier & 0x80) != 0;
365 
366         mCmdParams = new DisplayTextParams(cmdDet, textMsg);
367 
368         if (iconId != null) {
369             mloadIcon = true;
370             mIconLoadState = LOAD_SINGLE_ICON;
371             mIconLoader.loadIcon(iconId.recordNumber, this
372                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
373             return true;
374         }
375         return false;
376     }
377 
378     /**
379      * Processes SET_UP_IDLE_MODE_TEXT proactive command from the SIM card.
380      *
381      * @param cmdDet Command Details container object.
382      * @param ctlvs List of ComprehensionTlv objects following Command Details
383      *        object and Device Identities object within the proactive command
384      * @return true if the command is processing is pending and additional
385      *         asynchronous processing is required.
386      * @throws ResultException
387      */
processSetUpIdleModeText(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)388     private boolean processSetUpIdleModeText(CommandDetails cmdDet,
389             List<ComprehensionTlv> ctlvs) throws ResultException {
390 
391         CatLog.d(this, "process SetUpIdleModeText");
392 
393         TextMessage textMsg = new TextMessage();
394         IconId iconId = null;
395 
396         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING,
397                 ctlvs);
398         if (ctlv != null) {
399             textMsg.text = ValueParser.retrieveTextString(ctlv);
400         }
401 
402         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
403         if (ctlv != null) {
404             iconId = ValueParser.retrieveIconId(ctlv);
405             textMsg.iconSelfExplanatory = iconId.selfExplanatory;
406         }
407 
408         /*
409          * If the tlv object doesn't contain text and the icon is not self
410          * explanatory then reply with command not understood.
411          */
412 
413         if (textMsg.text == null && iconId != null && !textMsg.iconSelfExplanatory) {
414             throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
415         }
416         mCmdParams = new DisplayTextParams(cmdDet, textMsg);
417 
418         if (iconId != null) {
419             mloadIcon = true;
420             mIconLoadState = LOAD_SINGLE_ICON;
421             mIconLoader.loadIcon(iconId.recordNumber, this
422                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
423             return true;
424         }
425         return false;
426     }
427 
428     /**
429      * Processes GET_INKEY proactive command from the SIM card.
430      *
431      * @param cmdDet Command Details container object.
432      * @param ctlvs List of ComprehensionTlv objects following Command Details
433      *        object and Device Identities object within the proactive command
434      * @return true if the command is processing is pending and additional
435      *         asynchronous processing is required.
436      * @throws ResultException
437      */
processGetInkey(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)438     private boolean processGetInkey(CommandDetails cmdDet,
439             List<ComprehensionTlv> ctlvs) throws ResultException {
440 
441         CatLog.d(this, "process GetInkey");
442 
443         Input input = new Input();
444         IconId iconId = null;
445 
446         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING,
447                 ctlvs);
448         if (ctlv != null) {
449             input.text = ValueParser.retrieveTextString(ctlv);
450         } else {
451             throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
452         }
453         // parse icon identifier
454         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
455         if (ctlv != null) {
456             iconId = ValueParser.retrieveIconId(ctlv);
457         }
458 
459         // parse duration
460         ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
461         if (ctlv != null) {
462             input.duration = ValueParser.retrieveDuration(ctlv);
463         }
464 
465         input.minLen = 1;
466         input.maxLen = 1;
467 
468         input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0;
469         input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0;
470         input.yesNo = (cmdDet.commandQualifier & 0x04) != 0;
471         input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0;
472         input.echo = true;
473 
474         mCmdParams = new GetInputParams(cmdDet, input);
475 
476         if (iconId != null) {
477             mloadIcon = true;
478             mIconLoadState = LOAD_SINGLE_ICON;
479             mIconLoader.loadIcon(iconId.recordNumber, this
480                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
481             return true;
482         }
483         return false;
484     }
485 
486     /**
487      * Processes GET_INPUT proactive command from the SIM card.
488      *
489      * @param cmdDet Command Details container object.
490      * @param ctlvs List of ComprehensionTlv objects following Command Details
491      *        object and Device Identities object within the proactive command
492      * @return true if the command is processing is pending and additional
493      *         asynchronous processing is required.
494      * @throws ResultException
495      */
processGetInput(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)496     private boolean processGetInput(CommandDetails cmdDet,
497             List<ComprehensionTlv> ctlvs) throws ResultException {
498 
499         CatLog.d(this, "process GetInput");
500 
501         Input input = new Input();
502         IconId iconId = null;
503 
504         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING,
505                 ctlvs);
506         if (ctlv != null) {
507             input.text = ValueParser.retrieveTextString(ctlv);
508         } else {
509             throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
510         }
511 
512         ctlv = searchForTag(ComprehensionTlvTag.RESPONSE_LENGTH, ctlvs);
513         if (ctlv != null) {
514             try {
515                 byte[] rawValue = ctlv.getRawValue();
516                 int valueIndex = ctlv.getValueIndex();
517                 input.minLen = rawValue[valueIndex] & 0xff;
518                 input.maxLen = rawValue[valueIndex + 1] & 0xff;
519             } catch (IndexOutOfBoundsException e) {
520                 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
521             }
522         } else {
523             throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
524         }
525 
526         ctlv = searchForTag(ComprehensionTlvTag.DEFAULT_TEXT, ctlvs);
527         if (ctlv != null) {
528             input.defaultText = ValueParser.retrieveTextString(ctlv);
529         }
530         // parse icon identifier
531         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
532         if (ctlv != null) {
533             iconId = ValueParser.retrieveIconId(ctlv);
534         }
535 
536         input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0;
537         input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0;
538         input.echo = (cmdDet.commandQualifier & 0x04) == 0;
539         input.packed = (cmdDet.commandQualifier & 0x08) != 0;
540         input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0;
541 
542         // Truncate the maxLen if it exceeds the max number of chars that can
543         // be encoded. Limit depends on DCS in Command Qualifier.
544         if (input.ucs2 && input.maxLen > MAX_UCS2_CHARS) {
545             CatLog.d(this, "UCS2: received maxLen = " + input.maxLen +
546                   ", truncating to " + MAX_UCS2_CHARS);
547             input.maxLen = MAX_UCS2_CHARS;
548         } else if (!input.packed && input.maxLen > MAX_GSM7_DEFAULT_CHARS) {
549             CatLog.d(this, "GSM 7Bit Default: received maxLen = " + input.maxLen +
550                   ", truncating to " + MAX_GSM7_DEFAULT_CHARS);
551             input.maxLen = MAX_GSM7_DEFAULT_CHARS;
552         }
553 
554         mCmdParams = new GetInputParams(cmdDet, input);
555 
556         if (iconId != null) {
557             mloadIcon = true;
558             mIconLoadState = LOAD_SINGLE_ICON;
559             mIconLoader.loadIcon(iconId.recordNumber, this
560                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
561             return true;
562         }
563         return false;
564     }
565 
566     /**
567      * Processes REFRESH proactive command from the SIM card.
568      *
569      * @param cmdDet Command Details container object.
570      * @param ctlvs List of ComprehensionTlv objects following Command Details
571      *        object and Device Identities object within the proactive command
572      */
processRefresh(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)573     private boolean processRefresh(CommandDetails cmdDet,
574             List<ComprehensionTlv> ctlvs) {
575 
576         CatLog.d(this, "process Refresh");
577 
578         // REFRESH proactive command is rerouted by the baseband and handled by
579         // the telephony layer. IDLE TEXT should be removed for a REFRESH command
580         // with "initialization" or "reset"
581         switch (cmdDet.commandQualifier) {
582         case REFRESH_NAA_INIT_AND_FULL_FILE_CHANGE:
583         case REFRESH_NAA_INIT_AND_FILE_CHANGE:
584         case REFRESH_NAA_INIT:
585         case REFRESH_UICC_RESET:
586             mCmdParams = new DisplayTextParams(cmdDet, null);
587             break;
588         }
589         return false;
590     }
591 
592     /**
593      * Processes SELECT_ITEM proactive command from the SIM card.
594      *
595      * @param cmdDet Command Details container object.
596      * @param ctlvs List of ComprehensionTlv objects following Command Details
597      *        object and Device Identities object within the proactive command
598      * @return true if the command is processing is pending and additional
599      *         asynchronous processing is required.
600      * @throws ResultException
601      */
processSelectItem(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)602     private boolean processSelectItem(CommandDetails cmdDet,
603             List<ComprehensionTlv> ctlvs) throws ResultException {
604 
605         CatLog.d(this, "process SelectItem");
606 
607         Menu menu = new Menu();
608         IconId titleIconId = null;
609         ItemsIconId itemsIconId = null;
610         Iterator<ComprehensionTlv> iter = ctlvs.iterator();
611 
612         AppInterface.CommandType cmdType = AppInterface.CommandType
613                 .fromInt(cmdDet.typeOfCommand);
614 
615         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID,
616                 ctlvs);
617         if (ctlv != null) {
618             menu.title = ValueParser.retrieveAlphaId(ctlv);
619         } else if (cmdType == AppInterface.CommandType.SET_UP_MENU) {
620             // According to spec ETSI TS 102 223 section 6.10.3, the
621             // Alpha ID is mandatory (and also part of minimum set of
622             // elements required) for SET_UP_MENU. If it is not received
623             // by ME, then ME should respond with "error: missing minimum
624             // information" and not "command performed successfully".
625             throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
626         }
627 
628         while (true) {
629             ctlv = searchForNextTag(ComprehensionTlvTag.ITEM, iter);
630             if (ctlv != null) {
631                 menu.items.add(ValueParser.retrieveItem(ctlv));
632             } else {
633                 break;
634             }
635         }
636 
637         // We must have at least one menu item.
638         if (menu.items.size() == 0) {
639             throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
640         }
641 
642         ctlv = searchForTag(ComprehensionTlvTag.ITEM_ID, ctlvs);
643         if (ctlv != null) {
644             // CAT items are listed 1...n while list start at 0, need to
645             // subtract one.
646             menu.defaultItem = ValueParser.retrieveItemId(ctlv) - 1;
647         }
648 
649         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
650         if (ctlv != null) {
651             mIconLoadState = LOAD_SINGLE_ICON;
652             titleIconId = ValueParser.retrieveIconId(ctlv);
653             menu.titleIconSelfExplanatory = titleIconId.selfExplanatory;
654         }
655 
656         ctlv = searchForTag(ComprehensionTlvTag.ITEM_ICON_ID_LIST, ctlvs);
657         if (ctlv != null) {
658             mIconLoadState = LOAD_MULTI_ICONS;
659             itemsIconId = ValueParser.retrieveItemsIconId(ctlv);
660             menu.itemsIconSelfExplanatory = itemsIconId.selfExplanatory;
661         }
662 
663         boolean presentTypeSpecified = (cmdDet.commandQualifier & 0x01) != 0;
664         if (presentTypeSpecified) {
665             if ((cmdDet.commandQualifier & 0x02) == 0) {
666                 menu.presentationType = PresentationType.DATA_VALUES;
667             } else {
668                 menu.presentationType = PresentationType.NAVIGATION_OPTIONS;
669             }
670         }
671         menu.softKeyPreferred = (cmdDet.commandQualifier & 0x04) != 0;
672         menu.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0;
673 
674         mCmdParams = new SelectItemParams(cmdDet, menu, titleIconId != null);
675 
676         // Load icons data if needed.
677         switch(mIconLoadState) {
678         case LOAD_NO_ICON:
679             return false;
680         case LOAD_SINGLE_ICON:
681             mloadIcon = true;
682             mIconLoader.loadIcon(titleIconId.recordNumber, this
683                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
684             break;
685         case LOAD_MULTI_ICONS:
686             int[] recordNumbers = itemsIconId.recordNumbers;
687             if (titleIconId != null) {
688                 // Create a new array for all the icons (title and items).
689                 recordNumbers = new int[itemsIconId.recordNumbers.length + 1];
690                 recordNumbers[0] = titleIconId.recordNumber;
691                 System.arraycopy(itemsIconId.recordNumbers, 0, recordNumbers,
692                         1, itemsIconId.recordNumbers.length);
693             }
694             mloadIcon = true;
695             mIconLoader.loadIcons(recordNumbers, this
696                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
697             break;
698         }
699         return true;
700     }
701 
702     /**
703      * Processes EVENT_NOTIFY message from baseband.
704      *
705      * @param cmdDet Command Details container object.
706      * @param ctlvs List of ComprehensionTlv objects following Command Details
707      *        object and Device Identities object within the proactive command
708      * @return true if the command is processing is pending and additional
709      *         asynchronous processing is required.
710      */
processEventNotify(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)711     private boolean processEventNotify(CommandDetails cmdDet,
712             List<ComprehensionTlv> ctlvs) throws ResultException {
713 
714         CatLog.d(this, "process EventNotify");
715 
716         TextMessage textMsg = new TextMessage();
717         IconId iconId = null;
718 
719         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID,
720                 ctlvs);
721         textMsg.text = ValueParser.retrieveAlphaId(ctlv);
722 
723         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
724         if (ctlv != null) {
725             iconId = ValueParser.retrieveIconId(ctlv);
726             textMsg.iconSelfExplanatory = iconId.selfExplanatory;
727         }
728 
729         textMsg.responseNeeded = false;
730         mCmdParams = new DisplayTextParams(cmdDet, textMsg);
731 
732         if (iconId != null) {
733             mloadIcon = true;
734             mIconLoadState = LOAD_SINGLE_ICON;
735             mIconLoader.loadIcon(iconId.recordNumber, this
736                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
737             return true;
738         }
739         return false;
740     }
741 
742     /**
743      * Processes SET_UP_EVENT_LIST proactive command from the SIM card.
744      *
745      * @param cmdDet Command Details object retrieved.
746      * @param ctlvs List of ComprehensionTlv objects following Command Details
747      *        object and Device Identities object within the proactive command
748      * @return false. This function always returns false meaning that the command
749      *         processing is  not pending and additional asynchronous processing
750      *         is not required.
751      */
processSetUpEventList(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)752     private boolean processSetUpEventList(CommandDetails cmdDet,
753             List<ComprehensionTlv> ctlvs) {
754 
755         CatLog.d(this, "process SetUpEventList");
756         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.EVENT_LIST, ctlvs);
757         if (ctlv != null) {
758             try {
759                 byte[] rawValue = ctlv.getRawValue();
760                 int valueIndex = ctlv.getValueIndex();
761                 int valueLen = ctlv.getLength();
762                 int[] eventList = new int[valueLen];
763                 int eventValue = -1;
764                 int i = 0;
765                 while (valueLen > 0) {
766                     eventValue = rawValue[valueIndex] & 0xff;
767                     valueIndex++;
768                     valueLen--;
769 
770                     switch (eventValue) {
771                         case USER_ACTIVITY_EVENT:
772                         case IDLE_SCREEN_AVAILABLE_EVENT:
773                         case LANGUAGE_SELECTION_EVENT:
774                         case BROWSER_TERMINATION_EVENT:
775                         case BROWSING_STATUS_EVENT:
776                             eventList[i] = eventValue;
777                             i++;
778                             break;
779                         default:
780                             break;
781                     }
782 
783                 }
784                 mCmdParams = new SetEventListParams(cmdDet, eventList);
785             } catch (IndexOutOfBoundsException e) {
786                 CatLog.e(this, " IndexOutofBoundException in processSetUpEventList");
787             }
788         }
789         return false;
790     }
791 
792     /**
793      * Processes LAUNCH_BROWSER proactive command from the SIM card.
794      *
795      * @param cmdDet Command Details container object.
796      * @param ctlvs List of ComprehensionTlv objects following Command Details
797      *        object and Device Identities object within the proactive command
798      * @return true if the command is processing is pending and additional
799      *         asynchronous processing is required.
800      * @throws ResultException
801      */
processLaunchBrowser(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)802     private boolean processLaunchBrowser(CommandDetails cmdDet,
803             List<ComprehensionTlv> ctlvs) throws ResultException {
804 
805         CatLog.d(this, "process LaunchBrowser");
806 
807         TextMessage confirmMsg = new TextMessage();
808         IconId iconId = null;
809         String url = null;
810 
811         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.URL, ctlvs);
812         if (ctlv != null) {
813             try {
814                 byte[] rawValue = ctlv.getRawValue();
815                 int valueIndex = ctlv.getValueIndex();
816                 int valueLen = ctlv.getLength();
817                 if (valueLen > 0) {
818                     url = GsmAlphabet.gsm8BitUnpackedToString(rawValue,
819                             valueIndex, valueLen);
820                 } else {
821                     url = null;
822                 }
823             } catch (IndexOutOfBoundsException e) {
824                 throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
825             }
826         }
827 
828         // parse alpha identifier.
829         ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs);
830         confirmMsg.text = ValueParser.retrieveAlphaId(ctlv);
831 
832         // parse icon identifier
833         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
834         if (ctlv != null) {
835             iconId = ValueParser.retrieveIconId(ctlv);
836             confirmMsg.iconSelfExplanatory = iconId.selfExplanatory;
837         }
838 
839         // parse command qualifier value.
840         LaunchBrowserMode mode;
841         switch (cmdDet.commandQualifier) {
842         case 0x00:
843         default:
844             mode = LaunchBrowserMode.LAUNCH_IF_NOT_ALREADY_LAUNCHED;
845             break;
846         case 0x02:
847             mode = LaunchBrowserMode.USE_EXISTING_BROWSER;
848             break;
849         case 0x03:
850             mode = LaunchBrowserMode.LAUNCH_NEW_BROWSER;
851             break;
852         }
853 
854         mCmdParams = new LaunchBrowserParams(cmdDet, confirmMsg, url, mode);
855 
856         if (iconId != null) {
857             mIconLoadState = LOAD_SINGLE_ICON;
858             mIconLoader.loadIcon(iconId.recordNumber, this
859                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
860             return true;
861         }
862         return false;
863     }
864 
865      /**
866      * Processes PLAY_TONE proactive command from the SIM card.
867      *
868      * @param cmdDet Command Details container object.
869      * @param ctlvs List of ComprehensionTlv objects following Command Details
870      *        object and Device Identities object within the proactive command
871      * @return true if the command is processing is pending and additional
872      *         asynchronous processing is required.t
873      * @throws ResultException
874      */
processPlayTone(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)875     private boolean processPlayTone(CommandDetails cmdDet,
876             List<ComprehensionTlv> ctlvs) throws ResultException {
877 
878         CatLog.d(this, "process PlayTone");
879 
880         Tone tone = null;
881         TextMessage textMsg = new TextMessage();
882         Duration duration = null;
883         IconId iconId = null;
884 
885         ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TONE, ctlvs);
886         if (ctlv != null) {
887             // Nothing to do for null objects.
888             if (ctlv.getLength() > 0) {
889                 try {
890                     byte[] rawValue = ctlv.getRawValue();
891                     int valueIndex = ctlv.getValueIndex();
892                     int toneVal = rawValue[valueIndex];
893                     tone = Tone.fromInt(toneVal);
894                 } catch (IndexOutOfBoundsException e) {
895                     throw new ResultException(
896                             ResultCode.CMD_DATA_NOT_UNDERSTOOD);
897                 }
898             }
899         }
900         // parse alpha identifier
901         ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs);
902         if (ctlv != null) {
903             textMsg.text = ValueParser.retrieveAlphaId(ctlv);
904         }
905         // parse tone duration
906         ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
907         if (ctlv != null) {
908             duration = ValueParser.retrieveDuration(ctlv);
909         }
910         // parse icon identifier
911         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
912         if (ctlv != null) {
913             iconId = ValueParser.retrieveIconId(ctlv);
914             textMsg.iconSelfExplanatory = iconId.selfExplanatory;
915         }
916 
917         boolean vibrate = (cmdDet.commandQualifier & 0x01) != 0x00;
918 
919         textMsg.responseNeeded = false;
920         mCmdParams = new PlayToneParams(cmdDet, textMsg, tone, duration, vibrate);
921 
922         if (iconId != null) {
923             mIconLoadState = LOAD_SINGLE_ICON;
924             mIconLoader.loadIcon(iconId.recordNumber, this
925                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
926             return true;
927         }
928         return false;
929     }
930 
931     /**
932      * Processes SETUP_CALL proactive command from the SIM card.
933      *
934      * @param cmdDet Command Details object retrieved from the proactive command
935      *        object
936      * @param ctlvs List of ComprehensionTlv objects following Command Details
937      *        object and Device Identities object within the proactive command
938      * @return true if the command is processing is pending and additional
939      *         asynchronous processing is required.
940      */
processSetupCall(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)941     private boolean processSetupCall(CommandDetails cmdDet,
942             List<ComprehensionTlv> ctlvs) throws ResultException {
943         CatLog.d(this, "process SetupCall");
944 
945         Iterator<ComprehensionTlv> iter = ctlvs.iterator();
946         ComprehensionTlv ctlv = null;
947         // User confirmation phase message.
948         TextMessage confirmMsg = new TextMessage();
949         // Call set up phase message.
950         TextMessage callMsg = new TextMessage();
951         IconId confirmIconId = null;
952         IconId callIconId = null;
953 
954         // get confirmation message string.
955         ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter);
956         confirmMsg.text = ValueParser.retrieveAlphaId(ctlv);
957 
958         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
959         if (ctlv != null) {
960             confirmIconId = ValueParser.retrieveIconId(ctlv);
961             confirmMsg.iconSelfExplanatory = confirmIconId.selfExplanatory;
962         }
963 
964         // get call set up message string.
965         ctlv = searchForNextTag(ComprehensionTlvTag.ALPHA_ID, iter);
966         if (ctlv != null) {
967             callMsg.text = ValueParser.retrieveAlphaId(ctlv);
968         }
969 
970         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
971         if (ctlv != null) {
972             callIconId = ValueParser.retrieveIconId(ctlv);
973             callMsg.iconSelfExplanatory = callIconId.selfExplanatory;
974         }
975 
976         mCmdParams = new CallSetupParams(cmdDet, confirmMsg, callMsg);
977 
978         if (confirmIconId != null || callIconId != null) {
979             mIconLoadState = LOAD_MULTI_ICONS;
980             int[] recordNumbers = new int[2];
981             recordNumbers[0] = confirmIconId != null
982                     ? confirmIconId.recordNumber : -1;
983             recordNumbers[1] = callIconId != null ? callIconId.recordNumber
984                     : -1;
985 
986             mIconLoader.loadIcons(recordNumbers, this
987                     .obtainMessage(MSG_ID_LOAD_ICON_DONE));
988             return true;
989         }
990         return false;
991     }
992 
processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)993     private boolean processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)
994             throws ResultException {
995         CatLog.d(this, "process ProvideLocalInfo");
996         switch (cmdDet.commandQualifier) {
997             case DTTZ_SETTING:
998                 CatLog.d(this, "PLI [DTTZ_SETTING]");
999                 mCmdParams = new CommandParams(cmdDet);
1000                 break;
1001             case LANGUAGE_SETTING:
1002                 CatLog.d(this, "PLI [LANGUAGE_SETTING]");
1003                 mCmdParams = new CommandParams(cmdDet);
1004                 break;
1005             default:
1006                 CatLog.d(this, "PLI[" + cmdDet.commandQualifier + "] Command Not Supported");
1007                 mCmdParams = new CommandParams(cmdDet);
1008                 throw new ResultException(ResultCode.BEYOND_TERMINAL_CAPABILITY);
1009         }
1010         return false;
1011     }
1012 
processBIPClient(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)1013     private boolean processBIPClient(CommandDetails cmdDet,
1014                                      List<ComprehensionTlv> ctlvs) throws ResultException {
1015         AppInterface.CommandType commandType =
1016                                     AppInterface.CommandType.fromInt(cmdDet.typeOfCommand);
1017         if (commandType != null) {
1018             CatLog.d(this, "process "+ commandType.name());
1019         }
1020 
1021         TextMessage textMsg = new TextMessage();
1022         IconId iconId = null;
1023         ComprehensionTlv ctlv = null;
1024         boolean has_alpha_id = false;
1025 
1026         // parse alpha identifier
1027         ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs);
1028         if (ctlv != null) {
1029             textMsg.text = ValueParser.retrieveAlphaId(ctlv);
1030             CatLog.d(this, "alpha TLV text=" + textMsg.text);
1031             has_alpha_id = true;
1032         }
1033 
1034         // parse icon identifier
1035         ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
1036         if (ctlv != null) {
1037             iconId = ValueParser.retrieveIconId(ctlv);
1038             textMsg.iconSelfExplanatory = iconId.selfExplanatory;
1039         }
1040 
1041         textMsg.responseNeeded = false;
1042         mCmdParams = new BIPClientParams(cmdDet, textMsg, has_alpha_id);
1043 
1044         if (iconId != null) {
1045             mIconLoadState = LOAD_SINGLE_ICON;
1046             mIconLoader.loadIcon(iconId.recordNumber, obtainMessage(MSG_ID_LOAD_ICON_DONE));
1047             return true;
1048         }
1049         return false;
1050     }
1051 
dispose()1052     public void dispose() {
1053         mIconLoader.dispose();
1054         mIconLoader = null;
1055         mCmdParams = null;
1056         mCaller = null;
1057         sInstance = null;
1058     }
1059 }
1060