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