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