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