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