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