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.content.Context;
     20 import android.content.Intent;
     21 import android.os.AsyncResult;
     22 import android.os.Handler;
     23 import android.os.HandlerThread;
     24 import android.os.Message;
     25 import android.os.SystemProperties;
     26 
     27 import com.android.internal.telephony.IccUtils;
     28 import com.android.internal.telephony.CommandsInterface;
     29 import com.android.internal.telephony.IccCard;
     30 import com.android.internal.telephony.IccFileHandler;
     31 import com.android.internal.telephony.IccRecords;
     32 
     33 
     34 import java.io.ByteArrayOutputStream;
     35 import java.util.Locale;
     36 
     37 class RilMessage {
     38     int mId;
     39     Object mData;
     40     ResultCode mResCode;
     41 
     42     RilMessage(int msgId, String rawData) {
     43         mId = msgId;
     44         mData = rawData;
     45     }
     46 
     47     RilMessage(RilMessage other) {
     48         this.mId = other.mId;
     49         this.mData = other.mData;
     50         this.mResCode = other.mResCode;
     51     }
     52 }
     53 
     54 /**
     55  * Class that implements SIM Toolkit Telephony Service. Interacts with the RIL
     56  * and application.
     57  *
     58  * {@hide}
     59  */
     60 public class CatService extends Handler implements AppInterface {
     61 
     62     // Class members
     63     private static IccRecords mIccRecords;
     64 
     65     // Service members.
     66     // Protects singleton instance lazy initialization.
     67     private static final Object sInstanceLock = new Object();
     68     private static CatService sInstance;
     69     private CommandsInterface mCmdIf;
     70     private Context mContext;
     71     private CatCmdMessage mCurrntCmd = null;
     72     private CatCmdMessage mMenuCmd = null;
     73 
     74     private RilMessageDecoder mMsgDecoder = null;
     75 
     76     // Service constants.
     77     static final int MSG_ID_SESSION_END              = 1;
     78     static final int MSG_ID_PROACTIVE_COMMAND        = 2;
     79     static final int MSG_ID_EVENT_NOTIFY             = 3;
     80     static final int MSG_ID_CALL_SETUP               = 4;
     81     static final int MSG_ID_REFRESH                  = 5;
     82     static final int MSG_ID_RESPONSE                 = 6;
     83     static final int MSG_ID_SIM_READY                = 7;
     84 
     85     static final int MSG_ID_RIL_MSG_DECODED          = 10;
     86 
     87     // Events to signal SIM presence or absent in the device.
     88     private static final int MSG_ID_ICC_RECORDS_LOADED       = 20;
     89 
     90     private static final int DEV_ID_KEYPAD      = 0x01;
     91     private static final int DEV_ID_DISPLAY     = 0x02;
     92     private static final int DEV_ID_EARPIECE    = 0x03;
     93     private static final int DEV_ID_UICC        = 0x81;
     94     private static final int DEV_ID_TERMINAL    = 0x82;
     95     private static final int DEV_ID_NETWORK     = 0x83;
     96 
     97     static final String STK_DEFAULT = "Defualt Message";
     98 
     99     /* Intentionally private for singleton */
    100     private CatService(CommandsInterface ci, IccRecords ir, Context context,
    101             IccFileHandler fh, IccCard ic) {
    102         if (ci == null || ir == null || context == null || fh == null
    103                 || ic == null) {
    104             throw new NullPointerException(
    105                     "Service: Input parameters must not be null");
    106         }
    107         mCmdIf = ci;
    108         mContext = context;
    109 
    110         // Get the RilMessagesDecoder for decoding the messages.
    111         mMsgDecoder = RilMessageDecoder.getInstance(this, fh);
    112 
    113         // Register ril events handling.
    114         mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null);
    115         mCmdIf.setOnCatProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null);
    116         mCmdIf.setOnCatEvent(this, MSG_ID_EVENT_NOTIFY, null);
    117         mCmdIf.setOnCatCallSetUp(this, MSG_ID_CALL_SETUP, null);
    118         //mCmdIf.setOnSimRefresh(this, MSG_ID_REFRESH, null);
    119 
    120         mIccRecords = ir;
    121 
    122         // Register for SIM ready event.
    123         mCmdIf.registerForSIMReady(this, MSG_ID_SIM_READY, null);
    124         mCmdIf.registerForRUIMReady(this, MSG_ID_SIM_READY, null);
    125         mCmdIf.registerForNVReady(this, MSG_ID_SIM_READY, null);
    126         mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null);
    127 
    128         CatLog.d(this, "Is running");
    129     }
    130 
    131     public void dispose() {
    132         mIccRecords.unregisterForRecordsLoaded(this);
    133         mCmdIf.unSetOnCatSessionEnd(this);
    134         mCmdIf.unSetOnCatProactiveCmd(this);
    135         mCmdIf.unSetOnCatEvent(this);
    136         mCmdIf.unSetOnCatCallSetUp(this);
    137 
    138         this.removeCallbacksAndMessages(null);
    139     }
    140 
    141     protected void finalize() {
    142         CatLog.d(this, "Service finalized");
    143     }
    144 
    145     private void handleRilMsg(RilMessage rilMsg) {
    146         if (rilMsg == null) {
    147             return;
    148         }
    149 
    150         // dispatch messages
    151         CommandParams cmdParams = null;
    152         switch (rilMsg.mId) {
    153         case MSG_ID_EVENT_NOTIFY:
    154             if (rilMsg.mResCode == ResultCode.OK) {
    155                 cmdParams = (CommandParams) rilMsg.mData;
    156                 if (cmdParams != null) {
    157                     handleProactiveCommand(cmdParams);
    158                 }
    159             }
    160             break;
    161         case MSG_ID_PROACTIVE_COMMAND:
    162             try {
    163                 cmdParams = (CommandParams) rilMsg.mData;
    164             } catch (ClassCastException e) {
    165                 // for error handling : cast exception
    166                 CatLog.d(this, "Fail to parse proactive command");
    167                 sendTerminalResponse(mCurrntCmd.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD,
    168                                      false, 0x00, null);
    169                 break;
    170             }
    171             if (cmdParams != null) {
    172                 if (rilMsg.mResCode == ResultCode.OK) {
    173                     handleProactiveCommand(cmdParams);
    174                 } else {
    175                     // for proactive commands that couldn't be decoded
    176                     // successfully respond with the code generated by the
    177                     // message decoder.
    178                     sendTerminalResponse(cmdParams.cmdDet, rilMsg.mResCode,
    179                             false, 0, null);
    180                 }
    181             }
    182             break;
    183         case MSG_ID_REFRESH:
    184             cmdParams = (CommandParams) rilMsg.mData;
    185             if (cmdParams != null) {
    186                 handleProactiveCommand(cmdParams);
    187             }
    188             break;
    189         case MSG_ID_SESSION_END:
    190             handleSessionEnd();
    191             break;
    192         case MSG_ID_CALL_SETUP:
    193             // prior event notify command supplied all the information
    194             // needed for set up call processing.
    195             break;
    196         }
    197     }
    198 
    199     /**
    200      * Handles RIL_UNSOL_STK_PROACTIVE_COMMAND unsolicited command from RIL.
    201      * Sends valid proactive command data to the application using intents.
    202      *
    203      */
    204     private void handleProactiveCommand(CommandParams cmdParams) {
    205         CatLog.d(this, cmdParams.getCommandType().name());
    206 
    207         CharSequence message;
    208         CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams);
    209         switch (cmdParams.getCommandType()) {
    210             case SET_UP_MENU:
    211                 if (removeMenu(cmdMsg.getMenu())) {
    212                     mMenuCmd = null;
    213                 } else {
    214                     mMenuCmd = cmdMsg;
    215                 }
    216                 sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
    217                 break;
    218             case DISPLAY_TEXT:
    219                 // when application is not required to respond, send an immediate response.
    220                 if (!cmdMsg.geTextMessage().responseNeeded) {
    221                     sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
    222                 }
    223                 break;
    224             case REFRESH:
    225                 // ME side only handles refresh commands which meant to remove IDLE
    226                 // MODE TEXT.
    227                 cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value();
    228                 break;
    229             case SET_UP_IDLE_MODE_TEXT:
    230                 sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
    231                 break;
    232             case PROVIDE_LOCAL_INFORMATION:
    233                 ResponseData resp;
    234                 switch (cmdParams.cmdDet.commandQualifier) {
    235                     case CommandParamsFactory.DTTZ_SETTING:
    236                         resp = new DTTZResponseData(null);
    237                         sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
    238                         return;
    239                     case CommandParamsFactory.LANGUAGE_SETTING:
    240                         resp = new LanguageResponseData(Locale.getDefault().getLanguage());
    241                         sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
    242                         return;
    243                     default:
    244                         sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
    245                         return;
    246                 }
    247             case LAUNCH_BROWSER:
    248                 if ((((LaunchBrowserParams) cmdParams).confirmMsg.text != null)
    249                         && (((LaunchBrowserParams) cmdParams).confirmMsg.text.equals(STK_DEFAULT))) {
    250                     message = mContext.getText(com.android.internal.R.string.launchBrowserDefault);
    251                     ((LaunchBrowserParams) cmdParams).confirmMsg.text = message.toString();
    252                 }
    253                 break;
    254             case SELECT_ITEM:
    255             case GET_INPUT:
    256             case GET_INKEY:
    257                 break;
    258             case SEND_DTMF:
    259             case SEND_SMS:
    260             case SEND_SS:
    261             case SEND_USSD:
    262                 if ((((DisplayTextParams)cmdParams).textMsg.text != null)
    263                         && (((DisplayTextParams)cmdParams).textMsg.text.equals(STK_DEFAULT))) {
    264                     message = mContext.getText(com.android.internal.R.string.sending);
    265                     ((DisplayTextParams)cmdParams).textMsg.text = message.toString();
    266                 }
    267                 break;
    268             case PLAY_TONE:
    269                 break;
    270             case SET_UP_CALL:
    271                 if ((((CallSetupParams) cmdParams).confirmMsg.text != null)
    272                         && (((CallSetupParams) cmdParams).confirmMsg.text.equals(STK_DEFAULT))) {
    273                     message = mContext.getText(com.android.internal.R.string.SetupCallDefault);
    274                     ((CallSetupParams) cmdParams).confirmMsg.text = message.toString();
    275                 }
    276                 break;
    277             default:
    278                 CatLog.d(this, "Unsupported command");
    279                 return;
    280         }
    281         mCurrntCmd = cmdMsg;
    282         Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
    283         intent.putExtra("STK CMD", cmdMsg);
    284         mContext.sendBroadcast(intent);
    285     }
    286 
    287     /**
    288      * Handles RIL_UNSOL_STK_SESSION_END unsolicited command from RIL.
    289      *
    290      */
    291     private void handleSessionEnd() {
    292         CatLog.d(this, "SESSION END");
    293 
    294         mCurrntCmd = mMenuCmd;
    295         Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION);
    296         mContext.sendBroadcast(intent);
    297     }
    298 
    299     private void sendTerminalResponse(CommandDetails cmdDet,
    300             ResultCode resultCode, boolean includeAdditionalInfo,
    301             int additionalInfo, ResponseData resp) {
    302 
    303         if (cmdDet == null) {
    304             return;
    305         }
    306         ByteArrayOutputStream buf = new ByteArrayOutputStream();
    307 
    308         Input cmdInput = null;
    309         if (mCurrntCmd != null) {
    310             cmdInput = mCurrntCmd.geInput();
    311         }
    312 
    313         // command details
    314         int tag = ComprehensionTlvTag.COMMAND_DETAILS.value();
    315         if (cmdDet.compRequired) {
    316             tag |= 0x80;
    317         }
    318         buf.write(tag);
    319         buf.write(0x03); // length
    320         buf.write(cmdDet.commandNumber);
    321         buf.write(cmdDet.typeOfCommand);
    322         buf.write(cmdDet.commandQualifier);
    323 
    324         // device identities
    325         // According to TS102.223/TS31.111 section 6.8 Structure of
    326         // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N,
    327         // the ME should set the CR(comprehension required) flag to
    328         // comprehension not required.(CR=0)"
    329         // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N,
    330         // the CR flag is not set.
    331         tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value();
    332         buf.write(tag);
    333         buf.write(0x02); // length
    334         buf.write(DEV_ID_TERMINAL); // source device id
    335         buf.write(DEV_ID_UICC); // destination device id
    336 
    337         // result
    338         tag = 0x80 | ComprehensionTlvTag.RESULT.value();
    339         buf.write(tag);
    340         int length = includeAdditionalInfo ? 2 : 1;
    341         buf.write(length);
    342         buf.write(resultCode.value());
    343 
    344         // additional info
    345         if (includeAdditionalInfo) {
    346             buf.write(additionalInfo);
    347         }
    348 
    349         // Fill optional data for each corresponding command
    350         if (resp != null) {
    351             resp.format(buf);
    352         } else {
    353             encodeOptionalTags(cmdDet, resultCode, cmdInput, buf);
    354         }
    355 
    356         byte[] rawData = buf.toByteArray();
    357         String hexString = IccUtils.bytesToHexString(rawData);
    358         if (false) {
    359             CatLog.d(this, "TERMINAL RESPONSE: " + hexString);
    360         }
    361 
    362         mCmdIf.sendTerminalResponse(hexString, null);
    363     }
    364 
    365     private void encodeOptionalTags(CommandDetails cmdDet,
    366             ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) {
    367         CommandType cmdType = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand);
    368         if (cmdType != null) {
    369             switch (cmdType) {
    370                 case GET_INKEY:
    371                     // ETSI TS 102 384,27.22.4.2.8.4.2.
    372                     // If it is a response for GET_INKEY command and the response timeout
    373                     // occured, then add DURATION TLV for variable timeout case.
    374                     if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) &&
    375                         (cmdInput != null) && (cmdInput.duration != null)) {
    376                         getInKeyResponse(buf, cmdInput);
    377                     }
    378                     break;
    379                 case PROVIDE_LOCAL_INFORMATION:
    380                     if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) &&
    381                         (resultCode.value() == ResultCode.OK.value())) {
    382                         getPliResponse(buf);
    383                     }
    384                     break;
    385                 default:
    386                     CatLog.d(this, "encodeOptionalTags() Unsupported Cmd:" + cmdDet.typeOfCommand);
    387                     break;
    388             }
    389         } else {
    390             CatLog.d(this, "encodeOptionalTags() bad Cmd:" + cmdDet.typeOfCommand);
    391         }
    392     }
    393 
    394     private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) {
    395         int tag = ComprehensionTlvTag.DURATION.value();
    396 
    397         buf.write(tag);
    398         buf.write(0x02); // length
    399         buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds)
    400         buf.write(cmdInput.duration.timeInterval); // Time Duration
    401     }
    402 
    403     private void getPliResponse(ByteArrayOutputStream buf) {
    404 
    405         // Locale Language Setting
    406         String lang = SystemProperties.get("persist.sys.language");
    407 
    408         if (lang != null) {
    409             // tag
    410             int tag = ComprehensionTlvTag.LANGUAGE.value();
    411             buf.write(tag);
    412             ResponseData.writeLength(buf, lang.length());
    413             buf.write(lang.getBytes(), 0, lang.length());
    414         }
    415     }
    416 
    417     private void sendMenuSelection(int menuId, boolean helpRequired) {
    418 
    419         ByteArrayOutputStream buf = new ByteArrayOutputStream();
    420 
    421         // tag
    422         int tag = BerTlv.BER_MENU_SELECTION_TAG;
    423         buf.write(tag);
    424 
    425         // length
    426         buf.write(0x00); // place holder
    427 
    428         // device identities
    429         tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value();
    430         buf.write(tag);
    431         buf.write(0x02); // length
    432         buf.write(DEV_ID_KEYPAD); // source device id
    433         buf.write(DEV_ID_UICC); // destination device id
    434 
    435         // item identifier
    436         tag = 0x80 | ComprehensionTlvTag.ITEM_ID.value();
    437         buf.write(tag);
    438         buf.write(0x01); // length
    439         buf.write(menuId); // menu identifier chosen
    440 
    441         // help request
    442         if (helpRequired) {
    443             tag = ComprehensionTlvTag.HELP_REQUEST.value();
    444             buf.write(tag);
    445             buf.write(0x00); // length
    446         }
    447 
    448         byte[] rawData = buf.toByteArray();
    449 
    450         // write real length
    451         int len = rawData.length - 2; // minus (tag + length)
    452         rawData[1] = (byte) len;
    453 
    454         String hexString = IccUtils.bytesToHexString(rawData);
    455 
    456         mCmdIf.sendEnvelope(hexString, null);
    457     }
    458 
    459     private void eventDownload(int event, int sourceId, int destinationId,
    460             byte[] additionalInfo, boolean oneShot) {
    461 
    462         ByteArrayOutputStream buf = new ByteArrayOutputStream();
    463 
    464         // tag
    465         int tag = BerTlv.BER_EVENT_DOWNLOAD_TAG;
    466         buf.write(tag);
    467 
    468         // length
    469         buf.write(0x00); // place holder, assume length < 128.
    470 
    471         // event list
    472         tag = 0x80 | ComprehensionTlvTag.EVENT_LIST.value();
    473         buf.write(tag);
    474         buf.write(0x01); // length
    475         buf.write(event); // event value
    476 
    477         // device identities
    478         tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value();
    479         buf.write(tag);
    480         buf.write(0x02); // length
    481         buf.write(sourceId); // source device id
    482         buf.write(destinationId); // destination device id
    483 
    484         // additional information
    485         if (additionalInfo != null) {
    486             for (byte b : additionalInfo) {
    487                 buf.write(b);
    488             }
    489         }
    490 
    491         byte[] rawData = buf.toByteArray();
    492 
    493         // write real length
    494         int len = rawData.length - 2; // minus (tag + length)
    495         rawData[1] = (byte) len;
    496 
    497         String hexString = IccUtils.bytesToHexString(rawData);
    498 
    499         mCmdIf.sendEnvelope(hexString, null);
    500     }
    501 
    502     /**
    503      * Used for instantiating/updating the Service from the GsmPhone or CdmaPhone constructor.
    504      *
    505      * @param ci CommandsInterface object
    506      * @param ir IccRecords object
    507      * @param context phone app context
    508      * @param fh Icc file handler
    509      * @param ic Icc card
    510      * @return The only Service object in the system
    511      */
    512     public static CatService getInstance(CommandsInterface ci, IccRecords ir,
    513             Context context, IccFileHandler fh, IccCard ic) {
    514         synchronized (sInstanceLock) {
    515             if (sInstance == null) {
    516                 if (ci == null || ir == null || context == null || fh == null
    517                         || ic == null) {
    518                     return null;
    519                 }
    520                 HandlerThread thread = new HandlerThread("Cat Telephony service");
    521                 thread.start();
    522                 sInstance = new CatService(ci, ir, context, fh, ic);
    523                 CatLog.d(sInstance, "NEW sInstance");
    524             } else if ((ir != null) && (mIccRecords != ir)) {
    525                 CatLog.d(sInstance, "Reinitialize the Service with SIMRecords");
    526                 mIccRecords = ir;
    527 
    528                 // re-Register for SIM ready event.
    529                 mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null);
    530                 CatLog.d(sInstance, "sr changed reinitialize and return current sInstance");
    531             } else {
    532                 CatLog.d(sInstance, "Return current sInstance");
    533             }
    534             return sInstance;
    535         }
    536     }
    537 
    538     /**
    539      * Used by application to get an AppInterface object.
    540      *
    541      * @return The only Service object in the system
    542      */
    543     public static AppInterface getInstance() {
    544         return getInstance(null, null, null, null, null);
    545     }
    546 
    547     @Override
    548     public void handleMessage(Message msg) {
    549 
    550         switch (msg.what) {
    551         case MSG_ID_SESSION_END:
    552         case MSG_ID_PROACTIVE_COMMAND:
    553         case MSG_ID_EVENT_NOTIFY:
    554         case MSG_ID_REFRESH:
    555             CatLog.d(this, "ril message arrived");
    556             String data = null;
    557             if (msg.obj != null) {
    558                 AsyncResult ar = (AsyncResult) msg.obj;
    559                 if (ar != null && ar.result != null) {
    560                     try {
    561                         data = (String) ar.result;
    562                     } catch (ClassCastException e) {
    563                         break;
    564                     }
    565                 }
    566             }
    567             mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data));
    568             break;
    569         case MSG_ID_CALL_SETUP:
    570             mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null));
    571             break;
    572         case MSG_ID_ICC_RECORDS_LOADED:
    573             break;
    574         case MSG_ID_RIL_MSG_DECODED:
    575             handleRilMsg((RilMessage) msg.obj);
    576             break;
    577         case MSG_ID_RESPONSE:
    578             handleCmdResponse((CatResponseMessage) msg.obj);
    579             break;
    580         case MSG_ID_SIM_READY:
    581             CatLog.d(this, "SIM ready. Reporting STK service running now...");
    582             mCmdIf.reportStkServiceIsRunning(null);
    583             break;
    584         default:
    585             throw new AssertionError("Unrecognized CAT command: " + msg.what);
    586         }
    587     }
    588 
    589     public synchronized void onCmdResponse(CatResponseMessage resMsg) {
    590         if (resMsg == null) {
    591             return;
    592         }
    593         // queue a response message.
    594         Message msg = this.obtainMessage(MSG_ID_RESPONSE, resMsg);
    595         msg.sendToTarget();
    596     }
    597 
    598     private boolean validateResponse(CatResponseMessage resMsg) {
    599         if (mCurrntCmd != null) {
    600             return (resMsg.cmdDet.compareTo(mCurrntCmd.mCmdDet));
    601         }
    602         return false;
    603     }
    604 
    605     private boolean removeMenu(Menu menu) {
    606         try {
    607             if (menu.items.size() == 1 && menu.items.get(0) == null) {
    608                 return true;
    609             }
    610         } catch (NullPointerException e) {
    611             CatLog.d(this, "Unable to get Menu's items size");
    612             return true;
    613         }
    614         return false;
    615     }
    616 
    617     private void handleCmdResponse(CatResponseMessage resMsg) {
    618         // Make sure the response details match the last valid command. An invalid
    619         // response is a one that doesn't have a corresponding proactive command
    620         // and sending it can "confuse" the baseband/ril.
    621         // One reason for out of order responses can be UI glitches. For example,
    622         // if the application launch an activity, and that activity is stored
    623         // by the framework inside the history stack. That activity will be
    624         // available for relaunch using the latest application dialog
    625         // (long press on the home button). Relaunching that activity can send
    626         // the same command's result again to the CatService and can cause it to
    627         // get out of sync with the SIM.
    628         if (!validateResponse(resMsg)) {
    629             return;
    630         }
    631         ResponseData resp = null;
    632         boolean helpRequired = false;
    633         CommandDetails cmdDet = resMsg.getCmdDetails();
    634 
    635         switch (resMsg.resCode) {
    636         case HELP_INFO_REQUIRED:
    637             helpRequired = true;
    638             // fall through
    639         case OK:
    640         case PRFRMD_WITH_PARTIAL_COMPREHENSION:
    641         case PRFRMD_WITH_MISSING_INFO:
    642         case PRFRMD_WITH_ADDITIONAL_EFS_READ:
    643         case PRFRMD_ICON_NOT_DISPLAYED:
    644         case PRFRMD_MODIFIED_BY_NAA:
    645         case PRFRMD_LIMITED_SERVICE:
    646         case PRFRMD_WITH_MODIFICATION:
    647         case PRFRMD_NAA_NOT_ACTIVE:
    648         case PRFRMD_TONE_NOT_PLAYED:
    649             switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) {
    650             case SET_UP_MENU:
    651                 helpRequired = resMsg.resCode == ResultCode.HELP_INFO_REQUIRED;
    652                 sendMenuSelection(resMsg.usersMenuSelection, helpRequired);
    653                 return;
    654             case SELECT_ITEM:
    655                 resp = new SelectItemResponseData(resMsg.usersMenuSelection);
    656                 break;
    657             case GET_INPUT:
    658             case GET_INKEY:
    659                 Input input = mCurrntCmd.geInput();
    660                 if (!input.yesNo) {
    661                     // when help is requested there is no need to send the text
    662                     // string object.
    663                     if (!helpRequired) {
    664                         resp = new GetInkeyInputResponseData(resMsg.usersInput,
    665                                 input.ucs2, input.packed);
    666                     }
    667                 } else {
    668                     resp = new GetInkeyInputResponseData(
    669                             resMsg.usersYesNoSelection);
    670                 }
    671                 break;
    672             case DISPLAY_TEXT:
    673             case LAUNCH_BROWSER:
    674                 break;
    675             case SET_UP_CALL:
    676                 mCmdIf.handleCallSetupRequestFromSim(resMsg.usersConfirm, null);
    677                 // No need to send terminal response for SET UP CALL. The user's
    678                 // confirmation result is send back using a dedicated ril message
    679                 // invoked by the CommandInterface call above.
    680                 mCurrntCmd = null;
    681                 return;
    682             }
    683             break;
    684         case NO_RESPONSE_FROM_USER:
    685         case UICC_SESSION_TERM_BY_USER:
    686         case BACKWARD_MOVE_BY_USER:
    687             resp = null;
    688             break;
    689         default:
    690             return;
    691         }
    692         sendTerminalResponse(cmdDet, resMsg.resCode, false, 0, resp);
    693         mCurrntCmd = null;
    694     }
    695 }
    696