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 import android.telephony.SubscriptionManager;
     30 import android.telephony.TelephonyManager;
     31 
     32 import com.android.internal.telephony.CommandsInterface;
     33 import com.android.internal.telephony.PhoneConstants;
     34 import com.android.internal.telephony.SubscriptionController;
     35 import com.android.internal.telephony.uicc.IccFileHandler;
     36 import com.android.internal.telephony.uicc.IccRecords;
     37 import com.android.internal.telephony.uicc.IccUtils;
     38 import com.android.internal.telephony.uicc.UiccCard;
     39 import com.android.internal.telephony.uicc.UiccCardApplication;
     40 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
     41 import com.android.internal.telephony.uicc.IccRefreshResponse;
     42 import com.android.internal.telephony.uicc.UiccController;
     43 
     44 import java.io.ByteArrayOutputStream;
     45 import java.util.List;
     46 import java.util.Locale;
     47 
     48 import static com.android.internal.telephony.cat.CatCmdMessage.
     49                    SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT;
     50 import static com.android.internal.telephony.cat.CatCmdMessage.
     51                    SetupEventListConstants.LANGUAGE_SELECTION_EVENT;
     52 
     53 class RilMessage {
     54     int mId;
     55     Object mData;
     56     ResultCode mResCode;
     57 
     58     RilMessage(int msgId, String rawData) {
     59         mId = msgId;
     60         mData = rawData;
     61     }
     62 
     63     RilMessage(RilMessage other) {
     64         mId = other.mId;
     65         mData = other.mData;
     66         mResCode = other.mResCode;
     67     }
     68 }
     69 
     70 /**
     71  * Class that implements SIM Toolkit Telephony Service. Interacts with the RIL
     72  * and application.
     73  *
     74  * {@hide}
     75  */
     76 public class CatService extends Handler implements AppInterface {
     77     private static final boolean DBG = false;
     78 
     79     // Class members
     80     private static IccRecords mIccRecords;
     81     private static UiccCardApplication mUiccApplication;
     82 
     83     // Service members.
     84     // Protects singleton instance lazy initialization.
     85     private static final Object sInstanceLock = new Object();
     86     private static CatService[] sInstance = null;
     87     private CommandsInterface mCmdIf;
     88     private Context mContext;
     89     private CatCmdMessage mCurrntCmd = null;
     90     private CatCmdMessage mMenuCmd = null;
     91 
     92     private RilMessageDecoder mMsgDecoder = null;
     93     private boolean mStkAppInstalled = false;
     94 
     95     private UiccController mUiccController;
     96     private CardState mCardState = CardState.CARDSTATE_ABSENT;
     97 
     98     // Service constants.
     99     protected static final int MSG_ID_SESSION_END              = 1;
    100     protected static final int MSG_ID_PROACTIVE_COMMAND        = 2;
    101     protected static final int MSG_ID_EVENT_NOTIFY             = 3;
    102     protected static final int MSG_ID_CALL_SETUP               = 4;
    103     static final int MSG_ID_REFRESH                  = 5;
    104     static final int MSG_ID_RESPONSE                 = 6;
    105     static final int MSG_ID_SIM_READY                = 7;
    106 
    107     protected static final int MSG_ID_ICC_CHANGED    = 8;
    108     protected static final int MSG_ID_ALPHA_NOTIFY   = 9;
    109 
    110     static final int MSG_ID_RIL_MSG_DECODED          = 10;
    111 
    112     // Events to signal SIM presence or absent in the device.
    113     private static final int MSG_ID_ICC_RECORDS_LOADED       = 20;
    114 
    115     //Events to signal SIM REFRESH notificatations
    116     private static final int MSG_ID_ICC_REFRESH  = 30;
    117 
    118     private static final int DEV_ID_KEYPAD      = 0x01;
    119     private static final int DEV_ID_DISPLAY     = 0x02;
    120     private static final int DEV_ID_UICC        = 0x81;
    121     private static final int DEV_ID_TERMINAL    = 0x82;
    122     private static final int DEV_ID_NETWORK     = 0x83;
    123 
    124     static final String STK_DEFAULT = "Default Message";
    125 
    126     private HandlerThread mHandlerThread;
    127     private int mSlotId;
    128 
    129     /* For multisim catservice should not be singleton */
    130     private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir,
    131             Context context, IccFileHandler fh, UiccCard ic, int slotId) {
    132         if (ci == null || ca == null || ir == null || context == null || fh == null
    133                 || ic == null) {
    134             throw new NullPointerException(
    135                     "Service: Input parameters must not be null");
    136         }
    137         mCmdIf = ci;
    138         mContext = context;
    139         mSlotId = slotId;
    140         mHandlerThread = new HandlerThread("Cat Telephony service" + slotId);
    141         mHandlerThread.start();
    142 
    143         // Get the RilMessagesDecoder for decoding the messages.
    144         mMsgDecoder = RilMessageDecoder.getInstance(this, fh, slotId);
    145         if (null == mMsgDecoder) {
    146             CatLog.d(this, "Null RilMessageDecoder instance");
    147             return;
    148         }
    149         mMsgDecoder.start();
    150 
    151         // Register ril events handling.
    152         mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null);
    153         mCmdIf.setOnCatProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null);
    154         mCmdIf.setOnCatEvent(this, MSG_ID_EVENT_NOTIFY, null);
    155         mCmdIf.setOnCatCallSetUp(this, MSG_ID_CALL_SETUP, null);
    156         //mCmdIf.setOnSimRefresh(this, MSG_ID_REFRESH, null);
    157 
    158         mCmdIf.registerForIccRefresh(this, MSG_ID_ICC_REFRESH, null);
    159         mCmdIf.setOnCatCcAlphaNotify(this, MSG_ID_ALPHA_NOTIFY, null);
    160 
    161         mIccRecords = ir;
    162         mUiccApplication = ca;
    163 
    164         // Register for SIM ready event.
    165         mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null);
    166         CatLog.d(this, "registerForRecordsLoaded slotid=" + mSlotId + " instance:" + this);
    167 
    168 
    169         mUiccController = UiccController.getInstance();
    170         mUiccController.registerForIccChanged(this, MSG_ID_ICC_CHANGED, null);
    171 
    172         // Check if STK application is available
    173         mStkAppInstalled = isStkAppInstalled();
    174 
    175         CatLog.d(this, "Running CAT service on Slotid: " + mSlotId +
    176                 ". STK app installed:" + mStkAppInstalled);
    177     }
    178 
    179     /**
    180      * Used for instantiating the Service from the Card.
    181      *
    182      * @param ci CommandsInterface object
    183      * @param context phone app context
    184      * @param ic Icc card
    185      * @param slotId to know the index of card
    186      * @return The only Service object in the system
    187      */
    188     public static CatService getInstance(CommandsInterface ci,
    189             Context context, UiccCard ic, int slotId) {
    190         UiccCardApplication ca = null;
    191         IccFileHandler fh = null;
    192         IccRecords ir = null;
    193         if (ic != null) {
    194             /* Since Cat is not tied to any application, but rather is Uicc application
    195              * in itself - just get first FileHandler and IccRecords object
    196              */
    197             ca = ic.getApplicationIndex(0);
    198             if (ca != null) {
    199                 fh = ca.getIccFileHandler();
    200                 ir = ca.getIccRecords();
    201             }
    202         }
    203 
    204         synchronized (sInstanceLock) {
    205             if (sInstance == null) {
    206                 int simCount = TelephonyManager.getDefault().getSimCount();
    207                 sInstance = new CatService[simCount];
    208                 for (int i = 0; i < simCount; i++) {
    209                     sInstance[i] = null;
    210                 }
    211             }
    212             if (sInstance[slotId] == null) {
    213                 if (ci == null || ca == null || ir == null || context == null || fh == null
    214                         || ic == null) {
    215                     return null;
    216                 }
    217 
    218                 sInstance[slotId] = new CatService(ci, ca, ir, context, fh, ic, slotId);
    219             } else if ((ir != null) && (mIccRecords != ir)) {
    220                 if (mIccRecords != null) {
    221                     mIccRecords.unregisterForRecordsLoaded(sInstance[slotId]);
    222                 }
    223 
    224                 mIccRecords = ir;
    225                 mUiccApplication = ca;
    226 
    227                 mIccRecords.registerForRecordsLoaded(sInstance[slotId],
    228                         MSG_ID_ICC_RECORDS_LOADED, null);
    229                 CatLog.d(sInstance[slotId], "registerForRecordsLoaded slotid=" + slotId
    230                         + " instance:" + sInstance[slotId]);
    231             }
    232             return sInstance[slotId];
    233         }
    234     }
    235 
    236     public void dispose() {
    237         synchronized (sInstanceLock) {
    238             CatLog.d(this, "Disposing CatService object");
    239             mIccRecords.unregisterForRecordsLoaded(this);
    240 
    241             // Clean up stk icon if dispose is called
    242             broadcastCardStateAndIccRefreshResp(CardState.CARDSTATE_ABSENT, null);
    243 
    244             mCmdIf.unSetOnCatSessionEnd(this);
    245             mCmdIf.unSetOnCatProactiveCmd(this);
    246             mCmdIf.unSetOnCatEvent(this);
    247             mCmdIf.unSetOnCatCallSetUp(this);
    248             mCmdIf.unSetOnCatCcAlphaNotify(this);
    249 
    250             mCmdIf.unregisterForIccRefresh(this);
    251             if (mUiccController != null) {
    252                 mUiccController.unregisterForIccChanged(this);
    253                 mUiccController = null;
    254             }
    255             mMsgDecoder.dispose();
    256             mMsgDecoder = null;
    257             mHandlerThread.quit();
    258             mHandlerThread = null;
    259             removeCallbacksAndMessages(null);
    260             if (sInstance != null) {
    261                 if (SubscriptionManager.isValidSlotId(mSlotId)) {
    262                     sInstance[mSlotId] = null;
    263                 } else {
    264                     CatLog.d(this, "error: invaild slot id: " + mSlotId);
    265                 }
    266             }
    267         }
    268     }
    269 
    270     @Override
    271     protected void finalize() {
    272         CatLog.d(this, "Service finalized");
    273     }
    274 
    275     private void handleRilMsg(RilMessage rilMsg) {
    276         if (rilMsg == null) {
    277             return;
    278         }
    279 
    280         // dispatch messages
    281         CommandParams cmdParams = null;
    282         switch (rilMsg.mId) {
    283         case MSG_ID_EVENT_NOTIFY:
    284             if (rilMsg.mResCode == ResultCode.OK) {
    285                 cmdParams = (CommandParams) rilMsg.mData;
    286                 if (cmdParams != null) {
    287                     handleCommand(cmdParams, false);
    288                 }
    289             }
    290             break;
    291         case MSG_ID_PROACTIVE_COMMAND:
    292             try {
    293                 cmdParams = (CommandParams) rilMsg.mData;
    294             } catch (ClassCastException e) {
    295                 // for error handling : cast exception
    296                 CatLog.d(this, "Fail to parse proactive command");
    297                 // Don't send Terminal Resp if command detail is not available
    298                 if (mCurrntCmd != null) {
    299                     sendTerminalResponse(mCurrntCmd.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD,
    300                                      false, 0x00, null);
    301                 }
    302                 break;
    303             }
    304             if (cmdParams != null) {
    305                 if (rilMsg.mResCode == ResultCode.OK) {
    306                     handleCommand(cmdParams, true);
    307                 } else {
    308                     // for proactive commands that couldn't be decoded
    309                     // successfully respond with the code generated by the
    310                     // message decoder.
    311                     sendTerminalResponse(cmdParams.mCmdDet, rilMsg.mResCode,
    312                             false, 0, null);
    313                 }
    314             }
    315             break;
    316         case MSG_ID_REFRESH:
    317             cmdParams = (CommandParams) rilMsg.mData;
    318             if (cmdParams != null) {
    319                 handleCommand(cmdParams, false);
    320             }
    321             break;
    322         case MSG_ID_SESSION_END:
    323             handleSessionEnd();
    324             break;
    325         case MSG_ID_CALL_SETUP:
    326             // prior event notify command supplied all the information
    327             // needed for set up call processing.
    328             break;
    329         }
    330     }
    331 
    332     /**
    333      * This function validates the events in SETUP_EVENT_LIST which are currently
    334      * supported by the Android framework. In case of SETUP_EVENT_LIST has NULL events
    335      * or no events, all the events need to be reset.
    336      */
    337     private boolean isSupportedSetupEventCommand(CatCmdMessage cmdMsg) {
    338         boolean flag = true;
    339 
    340         for (int eventVal: cmdMsg.getSetEventList().eventList) {
    341             CatLog.d(this,"Event: " + eventVal);
    342             switch (eventVal) {
    343                 /* Currently android is supporting only the below events in SetupEventList
    344                  * Language Selection.  */
    345                 case IDLE_SCREEN_AVAILABLE_EVENT:
    346                 case LANGUAGE_SELECTION_EVENT:
    347                     break;
    348                 default:
    349                     flag = false;
    350             }
    351         }
    352         return flag;
    353     }
    354 
    355     /**
    356      * Handles RIL_UNSOL_STK_EVENT_NOTIFY or RIL_UNSOL_STK_PROACTIVE_COMMAND command
    357      * from RIL.
    358      * Sends valid proactive command data to the application using intents.
    359      * RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE will be send back if the command is
    360      * from RIL_UNSOL_STK_PROACTIVE_COMMAND.
    361      */
    362     private void handleCommand(CommandParams cmdParams, boolean isProactiveCmd) {
    363         CatLog.d(this, cmdParams.getCommandType().name());
    364 
    365         // Log all proactive commands.
    366         if (isProactiveCmd) {
    367             if (mUiccController != null) {
    368                 mUiccController.addCardLog("ProactiveCommand mSlotId=" + mSlotId +
    369                         " cmdParams=" + cmdParams);
    370             }
    371         }
    372 
    373         CharSequence message;
    374         CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams);
    375         switch (cmdParams.getCommandType()) {
    376             case SET_UP_MENU:
    377                 if (removeMenu(cmdMsg.getMenu())) {
    378                     mMenuCmd = null;
    379                 } else {
    380                     mMenuCmd = cmdMsg;
    381                 }
    382                 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
    383                 break;
    384             case DISPLAY_TEXT:
    385                 break;
    386             case REFRESH:
    387                 // ME side only handles refresh commands which meant to remove IDLE
    388                 // MODE TEXT.
    389                 cmdParams.mCmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value();
    390                 break;
    391             case SET_UP_IDLE_MODE_TEXT:
    392                 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
    393                 break;
    394             case SET_UP_EVENT_LIST:
    395                 if (isSupportedSetupEventCommand(cmdMsg)) {
    396                     sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
    397                 } else {
    398                     sendTerminalResponse(cmdParams.mCmdDet, ResultCode.BEYOND_TERMINAL_CAPABILITY,
    399                             false, 0, null);
    400                 }
    401                 break;
    402             case PROVIDE_LOCAL_INFORMATION:
    403                 ResponseData resp;
    404                 switch (cmdParams.mCmdDet.commandQualifier) {
    405                     case CommandParamsFactory.DTTZ_SETTING:
    406                         resp = new DTTZResponseData(null);
    407                         sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp);
    408                         break;
    409                     case CommandParamsFactory.LANGUAGE_SETTING:
    410                         resp = new LanguageResponseData(Locale.getDefault().getLanguage());
    411                         sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp);
    412                         break;
    413                     default:
    414                         sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
    415                 }
    416                 // No need to start STK app here.
    417                 return;
    418             case LAUNCH_BROWSER:
    419                 if ((((LaunchBrowserParams) cmdParams).mConfirmMsg.text != null)
    420                         && (((LaunchBrowserParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) {
    421                     message = mContext.getText(com.android.internal.R.string.launchBrowserDefault);
    422                     ((LaunchBrowserParams) cmdParams).mConfirmMsg.text = message.toString();
    423                 }
    424                 break;
    425             case SELECT_ITEM:
    426             case GET_INPUT:
    427             case GET_INKEY:
    428                 break;
    429             case SEND_DTMF:
    430             case SEND_SMS:
    431             case SEND_SS:
    432             case SEND_USSD:
    433                 if ((((DisplayTextParams)cmdParams).mTextMsg.text != null)
    434                         && (((DisplayTextParams)cmdParams).mTextMsg.text.equals(STK_DEFAULT))) {
    435                     message = mContext.getText(com.android.internal.R.string.sending);
    436                     ((DisplayTextParams)cmdParams).mTextMsg.text = message.toString();
    437                 }
    438                 break;
    439             case PLAY_TONE:
    440                 break;
    441             case SET_UP_CALL:
    442                 if ((((CallSetupParams) cmdParams).mConfirmMsg.text != null)
    443                         && (((CallSetupParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) {
    444                     message = mContext.getText(com.android.internal.R.string.SetupCallDefault);
    445                     ((CallSetupParams) cmdParams).mConfirmMsg.text = message.toString();
    446                 }
    447                 break;
    448             case OPEN_CHANNEL:
    449             case CLOSE_CHANNEL:
    450             case RECEIVE_DATA:
    451             case SEND_DATA:
    452                 BIPClientParams cmd = (BIPClientParams) cmdParams;
    453                 /* Per 3GPP specification 102.223,
    454                  * if the alpha identifier is not provided by the UICC,
    455                  * the terminal MAY give information to the user
    456                  * noAlphaUsrCnf defines if you need to show user confirmation or not
    457                  */
    458                 boolean noAlphaUsrCnf = false;
    459                 try {
    460                     noAlphaUsrCnf = mContext.getResources().getBoolean(
    461                             com.android.internal.R.bool.config_stkNoAlphaUsrCnf);
    462                 } catch (NotFoundException e) {
    463                     noAlphaUsrCnf = false;
    464                 }
    465                 if ((cmd.mTextMsg.text == null) && (cmd.mHasAlphaId || noAlphaUsrCnf)) {
    466                     CatLog.d(this, "cmd " + cmdParams.getCommandType() + " with null alpha id");
    467                     // If alpha length is zero, we just respond with OK.
    468                     if (isProactiveCmd) {
    469                         sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
    470                     } else if (cmdParams.getCommandType() == CommandType.OPEN_CHANNEL) {
    471                         mCmdIf.handleCallSetupRequestFromSim(true, null);
    472                     }
    473                     return;
    474                 }
    475                 // Respond with permanent failure to avoid retry if STK app is not present.
    476                 if (!mStkAppInstalled) {
    477                     CatLog.d(this, "No STK application found.");
    478                     if (isProactiveCmd) {
    479                         sendTerminalResponse(cmdParams.mCmdDet,
    480                                              ResultCode.BEYOND_TERMINAL_CAPABILITY,
    481                                              false, 0, null);
    482                         return;
    483                     }
    484                 }
    485                 /*
    486                  * CLOSE_CHANNEL, RECEIVE_DATA and SEND_DATA can be delivered by
    487                  * either PROACTIVE_COMMAND or EVENT_NOTIFY.
    488                  * If PROACTIVE_COMMAND is used for those commands, send terminal
    489                  * response here.
    490                  */
    491                 if (isProactiveCmd &&
    492                     ((cmdParams.getCommandType() == CommandType.CLOSE_CHANNEL) ||
    493                      (cmdParams.getCommandType() == CommandType.RECEIVE_DATA) ||
    494                      (cmdParams.getCommandType() == CommandType.SEND_DATA))) {
    495                     sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);
    496                 }
    497                 break;
    498             default:
    499                 CatLog.d(this, "Unsupported command");
    500                 return;
    501         }
    502         mCurrntCmd = cmdMsg;
    503         broadcastCatCmdIntent(cmdMsg);
    504     }
    505 
    506 
    507     private void broadcastCatCmdIntent(CatCmdMessage cmdMsg) {
    508         Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
    509         intent.putExtra("STK CMD", cmdMsg);
    510         intent.putExtra("SLOT_ID", mSlotId);
    511         CatLog.d(this, "Sending CmdMsg: " + cmdMsg+ " on slotid:" + mSlotId);
    512         mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
    513     }
    514 
    515     /**
    516      * Handles RIL_UNSOL_STK_SESSION_END unsolicited command from RIL.
    517      *
    518      */
    519     private void handleSessionEnd() {
    520         CatLog.d(this, "SESSION END on "+ mSlotId);
    521 
    522         mCurrntCmd = mMenuCmd;
    523         Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION);
    524         intent.putExtra("SLOT_ID", mSlotId);
    525         mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
    526     }
    527 
    528 
    529     private void sendTerminalResponse(CommandDetails cmdDet,
    530             ResultCode resultCode, boolean includeAdditionalInfo,
    531             int additionalInfo, ResponseData resp) {
    532 
    533         if (cmdDet == null) {
    534             return;
    535         }
    536         ByteArrayOutputStream buf = new ByteArrayOutputStream();
    537 
    538         Input cmdInput = null;
    539         if (mCurrntCmd != null) {
    540             cmdInput = mCurrntCmd.geInput();
    541         }
    542 
    543         // command details
    544         int tag = ComprehensionTlvTag.COMMAND_DETAILS.value();
    545         if (cmdDet.compRequired) {
    546             tag |= 0x80;
    547         }
    548         buf.write(tag);
    549         buf.write(0x03); // length
    550         buf.write(cmdDet.commandNumber);
    551         buf.write(cmdDet.typeOfCommand);
    552         buf.write(cmdDet.commandQualifier);
    553 
    554         // device identities
    555         // According to TS102.223/TS31.111 section 6.8 Structure of
    556         // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N,
    557         // the ME should set the CR(comprehension required) flag to
    558         // comprehension not required.(CR=0)"
    559         // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N,
    560         // the CR flag is not set.
    561         tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value();
    562         buf.write(tag);
    563         buf.write(0x02); // length
    564         buf.write(DEV_ID_TERMINAL); // source device id
    565         buf.write(DEV_ID_UICC); // destination device id
    566 
    567         // result
    568         tag = ComprehensionTlvTag.RESULT.value();
    569         if (cmdDet.compRequired) {
    570             tag |= 0x80;
    571         }
    572         buf.write(tag);
    573         int length = includeAdditionalInfo ? 2 : 1;
    574         buf.write(length);
    575         buf.write(resultCode.value());
    576 
    577         // additional info
    578         if (includeAdditionalInfo) {
    579             buf.write(additionalInfo);
    580         }
    581 
    582         // Fill optional data for each corresponding command
    583         if (resp != null) {
    584             resp.format(buf);
    585         } else {
    586             encodeOptionalTags(cmdDet, resultCode, cmdInput, buf);
    587         }
    588 
    589         byte[] rawData = buf.toByteArray();
    590         String hexString = IccUtils.bytesToHexString(rawData);
    591         if (DBG) {
    592             CatLog.d(this, "TERMINAL RESPONSE: " + hexString);
    593         }
    594 
    595         mCmdIf.sendTerminalResponse(hexString, null);
    596     }
    597 
    598     private void encodeOptionalTags(CommandDetails cmdDet,
    599             ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) {
    600         CommandType cmdType = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand);
    601         if (cmdType != null) {
    602             switch (cmdType) {
    603                 case GET_INKEY:
    604                     // ETSI TS 102 384,27.22.4.2.8.4.2.
    605                     // If it is a response for GET_INKEY command and the response timeout
    606                     // occured, then add DURATION TLV for variable timeout case.
    607                     if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) &&
    608                         (cmdInput != null) && (cmdInput.duration != null)) {
    609                         getInKeyResponse(buf, cmdInput);
    610                     }
    611                     break;
    612                 case PROVIDE_LOCAL_INFORMATION:
    613                     if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) &&
    614                         (resultCode.value() == ResultCode.OK.value())) {
    615                         getPliResponse(buf);
    616                     }
    617                     break;
    618                 default:
    619                     CatLog.d(this, "encodeOptionalTags() Unsupported Cmd details=" + cmdDet);
    620                     break;
    621             }
    622         } else {
    623             CatLog.d(this, "encodeOptionalTags() bad Cmd details=" + cmdDet);
    624         }
    625     }
    626 
    627     private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) {
    628         int tag = ComprehensionTlvTag.DURATION.value();
    629 
    630         buf.write(tag);
    631         buf.write(0x02); // length
    632         buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds)
    633         buf.write(cmdInput.duration.timeInterval); // Time Duration
    634     }
    635 
    636     private void getPliResponse(ByteArrayOutputStream buf) {
    637         // Locale Language Setting
    638         final String lang = Locale.getDefault().getLanguage();
    639 
    640         if (lang != null) {
    641             // tag
    642             int tag = ComprehensionTlvTag.LANGUAGE.value();
    643             buf.write(tag);
    644             ResponseData.writeLength(buf, lang.length());
    645             buf.write(lang.getBytes(), 0, lang.length());
    646         }
    647     }
    648 
    649     private void sendMenuSelection(int menuId, boolean helpRequired) {
    650 
    651         ByteArrayOutputStream buf = new ByteArrayOutputStream();
    652 
    653         // tag
    654         int tag = BerTlv.BER_MENU_SELECTION_TAG;
    655         buf.write(tag);
    656 
    657         // length
    658         buf.write(0x00); // place holder
    659 
    660         // device identities
    661         tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value();
    662         buf.write(tag);
    663         buf.write(0x02); // length
    664         buf.write(DEV_ID_KEYPAD); // source device id
    665         buf.write(DEV_ID_UICC); // destination device id
    666 
    667         // item identifier
    668         tag = 0x80 | ComprehensionTlvTag.ITEM_ID.value();
    669         buf.write(tag);
    670         buf.write(0x01); // length
    671         buf.write(menuId); // menu identifier chosen
    672 
    673         // help request
    674         if (helpRequired) {
    675             tag = ComprehensionTlvTag.HELP_REQUEST.value();
    676             buf.write(tag);
    677             buf.write(0x00); // length
    678         }
    679 
    680         byte[] rawData = buf.toByteArray();
    681 
    682         // write real length
    683         int len = rawData.length - 2; // minus (tag + length)
    684         rawData[1] = (byte) len;
    685 
    686         String hexString = IccUtils.bytesToHexString(rawData);
    687 
    688         mCmdIf.sendEnvelope(hexString, null);
    689     }
    690 
    691     private void eventDownload(int event, int sourceId, int destinationId,
    692             byte[] additionalInfo, boolean oneShot) {
    693 
    694         ByteArrayOutputStream buf = new ByteArrayOutputStream();
    695 
    696         // tag
    697         int tag = BerTlv.BER_EVENT_DOWNLOAD_TAG;
    698         buf.write(tag);
    699 
    700         // length
    701         buf.write(0x00); // place holder, assume length < 128.
    702 
    703         // event list
    704         tag = 0x80 | ComprehensionTlvTag.EVENT_LIST.value();
    705         buf.write(tag);
    706         buf.write(0x01); // length
    707         buf.write(event); // event value
    708 
    709         // device identities
    710         tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value();
    711         buf.write(tag);
    712         buf.write(0x02); // length
    713         buf.write(sourceId); // source device id
    714         buf.write(destinationId); // destination device id
    715 
    716         /*
    717          * Check for type of event download to be sent to UICC - Browser
    718          * termination,Idle screen available, User activity, Language selection
    719          * etc as mentioned under ETSI TS 102 223 section 7.5
    720          */
    721 
    722         /*
    723          * Currently the below events are supported:
    724          * Language Selection Event.
    725          * Other event download commands should be encoded similar way
    726          */
    727         /* TODO: eventDownload should be extended for other Envelope Commands */
    728         switch (event) {
    729             case IDLE_SCREEN_AVAILABLE_EVENT:
    730                 CatLog.d(sInstance, " Sending Idle Screen Available event download to ICC");
    731                 break;
    732             case LANGUAGE_SELECTION_EVENT:
    733                 CatLog.d(sInstance, " Sending Language Selection event download to ICC");
    734                 tag = 0x80 | ComprehensionTlvTag.LANGUAGE.value();
    735                 buf.write(tag);
    736                 // Language length should be 2 byte
    737                 buf.write(0x02);
    738                 break;
    739             default:
    740                 break;
    741         }
    742 
    743         // additional information
    744         if (additionalInfo != null) {
    745             for (byte b : additionalInfo) {
    746                 buf.write(b);
    747             }
    748         }
    749 
    750         byte[] rawData = buf.toByteArray();
    751 
    752         // write real length
    753         int len = rawData.length - 2; // minus (tag + length)
    754         rawData[1] = (byte) len;
    755 
    756         String hexString = IccUtils.bytesToHexString(rawData);
    757 
    758         CatLog.d(this, "ENVELOPE COMMAND: " + hexString);
    759 
    760         mCmdIf.sendEnvelope(hexString, null);
    761     }
    762 
    763     /**
    764      * Used by application to get an AppInterface object.
    765      *
    766      * @return The only Service object in the system
    767      */
    768     //TODO Need to take care for MSIM
    769     public static AppInterface getInstance() {
    770         int slotId = PhoneConstants.DEFAULT_CARD_INDEX;
    771         SubscriptionController sControl = SubscriptionController.getInstance();
    772         if (sControl != null) {
    773             slotId = sControl.getSlotId(sControl.getDefaultSubId());
    774         }
    775         return getInstance(null, null, null, slotId);
    776     }
    777 
    778     /**
    779      * Used by application to get an AppInterface object.
    780      *
    781      * @return The only Service object in the system
    782      */
    783     public static AppInterface getInstance(int slotId) {
    784         return getInstance(null, null, null, slotId);
    785     }
    786 
    787     @Override
    788     public void handleMessage(Message msg) {
    789         CatLog.d(this, "handleMessage[" + msg.what + "]");
    790 
    791         switch (msg.what) {
    792         case MSG_ID_SESSION_END:
    793         case MSG_ID_PROACTIVE_COMMAND:
    794         case MSG_ID_EVENT_NOTIFY:
    795         case MSG_ID_REFRESH:
    796             CatLog.d(this, "ril message arrived,slotid:" + mSlotId);
    797             String data = null;
    798             if (msg.obj != null) {
    799                 AsyncResult ar = (AsyncResult) msg.obj;
    800                 if (ar != null && ar.result != null) {
    801                     try {
    802                         data = (String) ar.result;
    803                     } catch (ClassCastException e) {
    804                         break;
    805                     }
    806                 }
    807             }
    808             mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data));
    809             break;
    810         case MSG_ID_CALL_SETUP:
    811             mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null));
    812             break;
    813         case MSG_ID_ICC_RECORDS_LOADED:
    814             break;
    815         case MSG_ID_RIL_MSG_DECODED:
    816             handleRilMsg((RilMessage) msg.obj);
    817             break;
    818         case MSG_ID_RESPONSE:
    819             handleCmdResponse((CatResponseMessage) msg.obj);
    820             break;
    821         case MSG_ID_ICC_CHANGED:
    822             CatLog.d(this, "MSG_ID_ICC_CHANGED");
    823             updateIccAvailability();
    824             break;
    825         case MSG_ID_ICC_REFRESH:
    826             if (msg.obj != null) {
    827                 AsyncResult ar = (AsyncResult) msg.obj;
    828                 if (ar != null && ar.result != null) {
    829                     broadcastCardStateAndIccRefreshResp(CardState.CARDSTATE_PRESENT,
    830                                   (IccRefreshResponse) ar.result);
    831                 } else {
    832                     CatLog.d(this,"Icc REFRESH with exception: " + ar.exception);
    833                 }
    834             } else {
    835                 CatLog.d(this, "IccRefresh Message is null");
    836             }
    837             break;
    838         case MSG_ID_ALPHA_NOTIFY:
    839             CatLog.d(this, "Received CAT CC Alpha message from card");
    840             if (msg.obj != null) {
    841                 AsyncResult ar = (AsyncResult) msg.obj;
    842                 if (ar != null && ar.result != null) {
    843                     broadcastAlphaMessage((String)ar.result);
    844                 } else {
    845                     CatLog.d(this, "CAT Alpha message: ar.result is null");
    846                 }
    847             } else {
    848                 CatLog.d(this, "CAT Alpha message: msg.obj is null");
    849             }
    850             break;
    851         default:
    852             throw new AssertionError("Unrecognized CAT command: " + msg.what);
    853         }
    854     }
    855 
    856     /**
    857      ** This function sends a CARD status (ABSENT, PRESENT, REFRESH) to STK_APP.
    858      ** This is triggered during ICC_REFRESH or CARD STATE changes. In case
    859      ** REFRESH, additional information is sent in 'refresh_result'
    860      **
    861      **/
    862     private void  broadcastCardStateAndIccRefreshResp(CardState cardState,
    863             IccRefreshResponse iccRefreshState) {
    864         Intent intent = new Intent(AppInterface.CAT_ICC_STATUS_CHANGE);
    865         boolean cardPresent = (cardState == CardState.CARDSTATE_PRESENT);
    866 
    867         if (iccRefreshState != null) {
    868             //This case is when MSG_ID_ICC_REFRESH is received.
    869             intent.putExtra(AppInterface.REFRESH_RESULT, iccRefreshState.refreshResult);
    870             CatLog.d(this, "Sending IccResult with Result: "
    871                     + iccRefreshState.refreshResult);
    872         }
    873 
    874         // This sends an intent with CARD_ABSENT (0 - false) /CARD_PRESENT (1 - true).
    875         intent.putExtra(AppInterface.CARD_STATUS, cardPresent);
    876         CatLog.d(this, "Sending Card Status: "
    877                 + cardState + " " + "cardPresent: " + cardPresent);
    878         mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
    879     }
    880 
    881     private void broadcastAlphaMessage(String alphaString) {
    882         CatLog.d(this, "Broadcasting CAT Alpha message from card: " + alphaString);
    883         Intent intent = new Intent(AppInterface.CAT_ALPHA_NOTIFY_ACTION);
    884         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    885         intent.putExtra(AppInterface.ALPHA_STRING, alphaString);
    886         intent.putExtra("SLOT_ID", mSlotId);
    887         mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
    888     }
    889 
    890     @Override
    891     public synchronized void onCmdResponse(CatResponseMessage resMsg) {
    892         if (resMsg == null) {
    893             return;
    894         }
    895         // queue a response message.
    896         Message msg = obtainMessage(MSG_ID_RESPONSE, resMsg);
    897         msg.sendToTarget();
    898     }
    899 
    900     private boolean validateResponse(CatResponseMessage resMsg) {
    901         boolean validResponse = false;
    902         if ((resMsg.mCmdDet.typeOfCommand == CommandType.SET_UP_EVENT_LIST.value())
    903                 || (resMsg.mCmdDet.typeOfCommand == CommandType.SET_UP_MENU.value())) {
    904             CatLog.d(this, "CmdType: " + resMsg.mCmdDet.typeOfCommand);
    905             validResponse = true;
    906         } else if (mCurrntCmd != null) {
    907             validResponse = resMsg.mCmdDet.compareTo(mCurrntCmd.mCmdDet);
    908             CatLog.d(this, "isResponse for last valid cmd: " + validResponse);
    909         }
    910         return validResponse;
    911     }
    912 
    913     private boolean removeMenu(Menu menu) {
    914         try {
    915             if (menu.items.size() == 1 && menu.items.get(0) == null) {
    916                 return true;
    917             }
    918         } catch (NullPointerException e) {
    919             CatLog.d(this, "Unable to get Menu's items size");
    920             return true;
    921         }
    922         return false;
    923     }
    924 
    925     private void handleCmdResponse(CatResponseMessage resMsg) {
    926         // Make sure the response details match the last valid command. An invalid
    927         // response is a one that doesn't have a corresponding proactive command
    928         // and sending it can "confuse" the baseband/ril.
    929         // One reason for out of order responses can be UI glitches. For example,
    930         // if the application launch an activity, and that activity is stored
    931         // by the framework inside the history stack. That activity will be
    932         // available for relaunch using the latest application dialog
    933         // (long press on the home button). Relaunching that activity can send
    934         // the same command's result again to the CatService and can cause it to
    935         // get out of sync with the SIM. This can happen in case of
    936         // non-interactive type Setup Event List and SETUP_MENU proactive commands.
    937         // Stk framework would have already sent Terminal Response to Setup Event
    938         // List and SETUP_MENU proactive commands. After sometime Stk app will send
    939         // Envelope Command/Event Download. In which case, the response details doesn't
    940         // match with last valid command (which are not related).
    941         // However, we should allow Stk framework to send the message to ICC.
    942         if (!validateResponse(resMsg)) {
    943             return;
    944         }
    945         ResponseData resp = null;
    946         boolean helpRequired = false;
    947         CommandDetails cmdDet = resMsg.getCmdDetails();
    948         AppInterface.CommandType type = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand);
    949 
    950         switch (resMsg.mResCode) {
    951         case HELP_INFO_REQUIRED:
    952             helpRequired = true;
    953             // fall through
    954         case OK:
    955         case PRFRMD_WITH_PARTIAL_COMPREHENSION:
    956         case PRFRMD_WITH_MISSING_INFO:
    957         case PRFRMD_WITH_ADDITIONAL_EFS_READ:
    958         case PRFRMD_ICON_NOT_DISPLAYED:
    959         case PRFRMD_MODIFIED_BY_NAA:
    960         case PRFRMD_LIMITED_SERVICE:
    961         case PRFRMD_WITH_MODIFICATION:
    962         case PRFRMD_NAA_NOT_ACTIVE:
    963         case PRFRMD_TONE_NOT_PLAYED:
    964         case LAUNCH_BROWSER_ERROR:
    965         case TERMINAL_CRNTLY_UNABLE_TO_PROCESS:
    966             switch (type) {
    967             case SET_UP_MENU:
    968                 helpRequired = resMsg.mResCode == ResultCode.HELP_INFO_REQUIRED;
    969                 sendMenuSelection(resMsg.mUsersMenuSelection, helpRequired);
    970                 return;
    971             case SELECT_ITEM:
    972                 resp = new SelectItemResponseData(resMsg.mUsersMenuSelection);
    973                 break;
    974             case GET_INPUT:
    975             case GET_INKEY:
    976                 Input input = mCurrntCmd.geInput();
    977                 if (!input.yesNo) {
    978                     // when help is requested there is no need to send the text
    979                     // string object.
    980                     if (!helpRequired) {
    981                         resp = new GetInkeyInputResponseData(resMsg.mUsersInput,
    982                                 input.ucs2, input.packed);
    983                     }
    984                 } else {
    985                     resp = new GetInkeyInputResponseData(
    986                             resMsg.mUsersYesNoSelection);
    987                 }
    988                 break;
    989             case DISPLAY_TEXT:
    990                 if (resMsg.mResCode == ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS) {
    991                     // For screenbusy case there will be addtional information in the terminal
    992                     // response. And the value of the additional information byte is 0x01.
    993                     resMsg.setAdditionalInfo(0x01);
    994                 } else {
    995                     resMsg.mIncludeAdditionalInfo = false;
    996                     resMsg.mAdditionalInfo = 0;
    997                 }
    998                 break;
    999             case LAUNCH_BROWSER:
   1000                 break;
   1001             // 3GPP TS.102.223: Open Channel alpha confirmation should not send TR
   1002             case OPEN_CHANNEL:
   1003             case SET_UP_CALL:
   1004                 mCmdIf.handleCallSetupRequestFromSim(resMsg.mUsersConfirm, null);
   1005                 // No need to send terminal response for SET UP CALL. The user's
   1006                 // confirmation result is send back using a dedicated ril message
   1007                 // invoked by the CommandInterface call above.
   1008                 mCurrntCmd = null;
   1009                 return;
   1010             case SET_UP_EVENT_LIST:
   1011                 if (IDLE_SCREEN_AVAILABLE_EVENT == resMsg.mEventValue) {
   1012                     eventDownload(resMsg.mEventValue, DEV_ID_DISPLAY, DEV_ID_UICC,
   1013                             resMsg.mAddedInfo, false);
   1014                  } else {
   1015                      eventDownload(resMsg.mEventValue, DEV_ID_TERMINAL, DEV_ID_UICC,
   1016                             resMsg.mAddedInfo, false);
   1017                  }
   1018                 // No need to send the terminal response after event download.
   1019                 return;
   1020             default:
   1021                 break;
   1022             }
   1023             break;
   1024         case BACKWARD_MOVE_BY_USER:
   1025         case USER_NOT_ACCEPT:
   1026             // if the user dismissed the alert dialog for a
   1027             // setup call/open channel, consider that as the user
   1028             // rejecting the call. Use dedicated API for this, rather than
   1029             // sending a terminal response.
   1030             if (type == CommandType.SET_UP_CALL || type == CommandType.OPEN_CHANNEL) {
   1031                 mCmdIf.handleCallSetupRequestFromSim(false, null);
   1032                 mCurrntCmd = null;
   1033                 return;
   1034             } else {
   1035                 resp = null;
   1036             }
   1037             break;
   1038         case NO_RESPONSE_FROM_USER:
   1039         case UICC_SESSION_TERM_BY_USER:
   1040             resp = null;
   1041             break;
   1042         default:
   1043             return;
   1044         }
   1045         sendTerminalResponse(cmdDet, resMsg.mResCode, resMsg.mIncludeAdditionalInfo,
   1046                 resMsg.mAdditionalInfo, resp);
   1047         mCurrntCmd = null;
   1048     }
   1049 
   1050     private boolean isStkAppInstalled() {
   1051         Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
   1052         PackageManager pm = mContext.getPackageManager();
   1053         List<ResolveInfo> broadcastReceivers =
   1054                             pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA);
   1055         int numReceiver = broadcastReceivers == null ? 0 : broadcastReceivers.size();
   1056 
   1057         return (numReceiver > 0);
   1058     }
   1059 
   1060     public void update(CommandsInterface ci,
   1061             Context context, UiccCard ic) {
   1062         UiccCardApplication ca = null;
   1063         IccRecords ir = null;
   1064 
   1065         if (ic != null) {
   1066             /* Since Cat is not tied to any application, but rather is Uicc application
   1067              * in itself - just get first FileHandler and IccRecords object
   1068              */
   1069             ca = ic.getApplicationIndex(0);
   1070             if (ca != null) {
   1071                 ir = ca.getIccRecords();
   1072             }
   1073         }
   1074 
   1075         synchronized (sInstanceLock) {
   1076             if ((ir != null) && (mIccRecords != ir)) {
   1077                 if (mIccRecords != null) {
   1078                     mIccRecords.unregisterForRecordsLoaded(this);
   1079                 }
   1080 
   1081                 CatLog.d(this,
   1082                         "Reinitialize the Service with SIMRecords and UiccCardApplication");
   1083                 mIccRecords = ir;
   1084                 mUiccApplication = ca;
   1085 
   1086                 // re-Register for SIM ready event.
   1087                 mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null);
   1088                 CatLog.d(this, "registerForRecordsLoaded slotid=" + mSlotId + " instance:" + this);
   1089             }
   1090         }
   1091     }
   1092 
   1093     void updateIccAvailability() {
   1094         if (null == mUiccController) {
   1095             return;
   1096         }
   1097 
   1098         CardState newState = CardState.CARDSTATE_ABSENT;
   1099         UiccCard newCard = mUiccController.getUiccCard(mSlotId);
   1100         if (newCard != null) {
   1101             newState = newCard.getCardState();
   1102         }
   1103         CardState oldState = mCardState;
   1104         mCardState = newState;
   1105         CatLog.d(this,"New Card State = " + newState + " " + "Old Card State = " + oldState);
   1106         if (oldState == CardState.CARDSTATE_PRESENT &&
   1107                 newState != CardState.CARDSTATE_PRESENT) {
   1108             broadcastCardStateAndIccRefreshResp(newState, null);
   1109         } else if (oldState != CardState.CARDSTATE_PRESENT &&
   1110                 newState == CardState.CARDSTATE_PRESENT) {
   1111             // Card moved to PRESENT STATE.
   1112             mCmdIf.reportStkServiceIsRunning(null);
   1113         }
   1114     }
   1115 }
   1116