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