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