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