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