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