Home | History | Annotate | Download | only in stk
      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.stk;
     18 
     19 import android.app.ActivityManager;
     20 import android.app.ActivityManager.RunningTaskInfo;
     21 import android.app.AlertDialog;
     22 import android.app.Notification;
     23 import android.app.NotificationManager;
     24 import android.app.PendingIntent;
     25 import android.app.Service;
     26 import android.app.Activity;
     27 import android.app.ActivityManager;
     28 import android.app.ActivityManager.RecentTaskInfo;
     29 import android.app.ActivityManager.RunningAppProcessInfo;
     30 import android.content.ComponentName;
     31 import android.content.Context;
     32 import android.content.DialogInterface;
     33 import android.content.Intent;
     34 import android.content.res.Configuration;
     35 import android.graphics.Bitmap;
     36 import android.graphics.BitmapFactory;
     37 import android.net.Uri;
     38 import android.os.Bundle;
     39 import android.os.Handler;
     40 import android.os.IBinder;
     41 import android.os.Looper;
     42 import android.os.Message;
     43 import android.os.PowerManager;
     44 import android.os.SystemProperties;
     45 import android.provider.Settings;
     46 import android.telephony.TelephonyManager;
     47 import android.text.TextUtils;
     48 import android.view.Gravity;
     49 import android.view.LayoutInflater;
     50 import android.view.View;
     51 import android.view.Window;
     52 import android.view.WindowManager;
     53 import android.widget.ImageView;
     54 import android.widget.RemoteViews;
     55 import android.widget.TextView;
     56 import android.widget.Toast;
     57 import android.content.BroadcastReceiver;
     58 import android.content.IntentFilter;
     59 import android.content.pm.ApplicationInfo;
     60 import android.content.pm.PackageManager.NameNotFoundException;
     61 
     62 import com.android.internal.telephony.cat.AppInterface;
     63 import com.android.internal.telephony.cat.LaunchBrowserMode;
     64 import com.android.internal.telephony.cat.Menu;
     65 import com.android.internal.telephony.cat.Item;
     66 import com.android.internal.telephony.cat.Input;
     67 import com.android.internal.telephony.cat.ResultCode;
     68 import com.android.internal.telephony.cat.CatCmdMessage;
     69 import com.android.internal.telephony.cat.CatCmdMessage.BrowserSettings;
     70 import com.android.internal.telephony.cat.CatCmdMessage.SetupEventListSettings;
     71 import com.android.internal.telephony.cat.CatLog;
     72 import com.android.internal.telephony.cat.CatResponseMessage;
     73 import com.android.internal.telephony.cat.TextMessage;
     74 import com.android.internal.telephony.uicc.IccRefreshResponse;
     75 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
     76 import com.android.internal.telephony.PhoneConstants;
     77 import com.android.internal.telephony.TelephonyIntents;
     78 import com.android.internal.telephony.IccCardConstants;
     79 import com.android.internal.telephony.uicc.UiccController;
     80 import com.android.internal.telephony.GsmAlphabet;
     81 import com.android.internal.telephony.cat.CatService;
     82 
     83 import java.util.Iterator;
     84 import java.util.LinkedList;
     85 import java.lang.System;
     86 import java.util.List;
     87 
     88 import static com.android.internal.telephony.cat.CatCmdMessage.
     89                    SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT;
     90 import static com.android.internal.telephony.cat.CatCmdMessage.
     91                    SetupEventListConstants.LANGUAGE_SELECTION_EVENT;
     92 
     93 /**
     94  * SIM toolkit application level service. Interacts with Telephopny messages,
     95  * application's launch and user input from STK UI elements.
     96  *
     97  */
     98 public class StkAppService extends Service implements Runnable {
     99 
    100     // members
    101     protected class StkContext {
    102         protected CatCmdMessage mMainCmd = null;
    103         protected CatCmdMessage mCurrentCmd = null;
    104         protected CatCmdMessage mCurrentMenuCmd = null;
    105         protected Menu mCurrentMenu = null;
    106         protected String lastSelectedItem = null;
    107         protected boolean mMenuIsVisible = false;
    108         protected boolean mIsInputPending = false;
    109         protected boolean mIsMenuPending = false;
    110         protected boolean mIsDialogPending = false;
    111         protected boolean responseNeeded = true;
    112         protected boolean launchBrowser = false;
    113         protected BrowserSettings mBrowserSettings = null;
    114         protected LinkedList<DelayedCmd> mCmdsQ = null;
    115         protected boolean mCmdInProgress = false;
    116         protected int mStkServiceState = STATE_UNKNOWN;
    117         protected int mSetupMenuState = STATE_UNKNOWN;
    118         protected int mMenuState = StkMenuActivity.STATE_INIT;
    119         protected int mOpCode = -1;
    120         private Activity mActivityInstance = null;
    121         private Activity mDialogInstance = null;
    122         private Activity mMainActivityInstance = null;
    123         private int mSlotId = 0;
    124         private SetupEventListSettings mSetupEventListSettings = null;
    125         private boolean mClearSelectItem = false;
    126         private boolean mDisplayTextDlgIsVisibile = false;
    127         private CatCmdMessage mCurrentSetupEventCmd = null;
    128         private CatCmdMessage mIdleModeTextCmd = null;
    129         final synchronized void setPendingActivityInstance(Activity act) {
    130             CatLog.d(this, "setPendingActivityInstance act : " + mSlotId + ", " + act);
    131             callSetActivityInstMsg(OP_SET_ACT_INST, mSlotId, act);
    132         }
    133         final synchronized Activity getPendingActivityInstance() {
    134             CatLog.d(this, "getPendingActivityInstance act : " + mSlotId + ", " +
    135                     mActivityInstance);
    136             return mActivityInstance;
    137         }
    138         final synchronized void setPendingDialogInstance(Activity act) {
    139             CatLog.d(this, "setPendingDialogInstance act : " + mSlotId + ", " + act);
    140             callSetActivityInstMsg(OP_SET_DAL_INST, mSlotId, act);
    141         }
    142         final synchronized Activity getPendingDialogInstance() {
    143             CatLog.d(this, "getPendingDialogInstance act : " + mSlotId + ", " +
    144                     mDialogInstance);
    145             return mDialogInstance;
    146         }
    147         final synchronized void setMainActivityInstance(Activity act) {
    148             CatLog.d(this, "setMainActivityInstance act : " + mSlotId + ", " + act);
    149             callSetActivityInstMsg(OP_SET_MAINACT_INST, mSlotId, act);
    150         }
    151         final synchronized Activity getMainActivityInstance() {
    152             CatLog.d(this, "getMainActivityInstance act : " + mSlotId + ", " +
    153                     mMainActivityInstance);
    154             return mMainActivityInstance;
    155         }
    156     }
    157 
    158     private volatile Looper mServiceLooper;
    159     private volatile ServiceHandler mServiceHandler;
    160     private Context mContext = null;
    161     private NotificationManager mNotificationManager = null;
    162     static StkAppService sInstance = null;
    163     private AppInterface[] mStkService = null;
    164     private StkContext[] mStkContext = null;
    165     private int mSimCount = 0;
    166     private PowerManager mPowerManager = null;
    167     private StkCmdReceiver mStkCmdReceiver = null;
    168 
    169     // Used for setting FLAG_ACTIVITY_NO_USER_ACTION when
    170     // creating an intent.
    171     private enum InitiatedByUserAction {
    172         yes,            // The action was started via a user initiated action
    173         unknown,        // Not known for sure if user initated the action
    174     }
    175 
    176     // constants
    177     static final String OPCODE = "op";
    178     static final String CMD_MSG = "cmd message";
    179     static final String RES_ID = "response id";
    180     static final String MENU_SELECTION = "menu selection";
    181     static final String INPUT = "input";
    182     static final String HELP = "help";
    183     static final String CONFIRMATION = "confirm";
    184     static final String CHOICE = "choice";
    185     static final String SLOT_ID = "SLOT_ID";
    186     static final String STK_CMD = "STK CMD";
    187     static final String STK_DIALOG_URI = "stk://com.android.stk/dialog/";
    188     static final String STK_MENU_URI = "stk://com.android.stk/menu/";
    189     static final String STK_INPUT_URI = "stk://com.android.stk/input/";
    190     static final String STK_TONE_URI = "stk://com.android.stk/tone/";
    191 
    192     // These below constants are used for SETUP_EVENT_LIST
    193     static final String SETUP_EVENT_TYPE = "event";
    194     static final String SETUP_EVENT_CAUSE = "cause";
    195 
    196     // operations ids for different service functionality.
    197     static final int OP_CMD = 1;
    198     static final int OP_RESPONSE = 2;
    199     static final int OP_LAUNCH_APP = 3;
    200     static final int OP_END_SESSION = 4;
    201     static final int OP_BOOT_COMPLETED = 5;
    202     private static final int OP_DELAYED_MSG = 6;
    203     static final int OP_CARD_STATUS_CHANGED = 7;
    204     static final int OP_SET_ACT_INST = 8;
    205     static final int OP_SET_DAL_INST = 9;
    206     static final int OP_SET_MAINACT_INST = 10;
    207     static final int OP_LOCALE_CHANGED = 11;
    208     static final int OP_ALPHA_NOTIFY = 12;
    209     static final int OP_IDLE_SCREEN = 13;
    210 
    211     //Invalid SetupEvent
    212     static final int INVALID_SETUP_EVENT = 0xFF;
    213 
    214     // Response ids
    215     static final int RES_ID_MENU_SELECTION = 11;
    216     static final int RES_ID_INPUT = 12;
    217     static final int RES_ID_CONFIRM = 13;
    218     static final int RES_ID_DONE = 14;
    219     static final int RES_ID_CHOICE = 15;
    220 
    221     static final int RES_ID_TIMEOUT = 20;
    222     static final int RES_ID_BACKWARD = 21;
    223     static final int RES_ID_END_SESSION = 22;
    224     static final int RES_ID_EXIT = 23;
    225 
    226     static final int YES = 1;
    227     static final int NO = 0;
    228 
    229     static final int STATE_UNKNOWN = -1;
    230     static final int STATE_NOT_EXIST = 0;
    231     static final int STATE_EXIST = 1;
    232 
    233     private static final String PACKAGE_NAME = "com.android.stk";
    234     private static final String STK_MENU_ACTIVITY_NAME = PACKAGE_NAME + ".StkMenuActivity";
    235     private static final String STK_INPUT_ACTIVITY_NAME = PACKAGE_NAME + ".StkInputActivity";
    236     private static final String STK_DIALOG_ACTIVITY_NAME = PACKAGE_NAME + ".StkDialogActivity";
    237     // Notification id used to display Idle Mode text in NotificationManager.
    238     private static final int STK_NOTIFICATION_ID = 333;
    239     private static final String LOG_TAG = new Object(){}.getClass().getEnclosingClass().getName();
    240 
    241     // Inner class used for queuing telephony messages (proactive commands,
    242     // session end) while the service is busy processing a previous message.
    243     private class DelayedCmd {
    244         // members
    245         int id;
    246         CatCmdMessage msg;
    247         int slotId;
    248 
    249         DelayedCmd(int id, CatCmdMessage msg, int slotId) {
    250             this.id = id;
    251             this.msg = msg;
    252             this.slotId = slotId;
    253         }
    254     }
    255 
    256     // system property to set the STK specific default url for launch browser proactive cmds
    257     private static final String STK_BROWSER_DEFAULT_URL_SYSPROP = "persist.radio.stk.default_url";
    258 
    259     @Override
    260     public void onCreate() {
    261         CatLog.d(LOG_TAG, "onCreate()+");
    262         // Initialize members
    263         int i = 0;
    264         mContext = getBaseContext();
    265         mSimCount = TelephonyManager.from(mContext).getSimCount();
    266         CatLog.d(LOG_TAG, "simCount: " + mSimCount);
    267         mStkService = new AppInterface[mSimCount];
    268         mStkContext = new StkContext[mSimCount];
    269         mPowerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
    270         mStkCmdReceiver = new StkCmdReceiver();
    271         registerReceiver(mStkCmdReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
    272         for (i = 0; i < mSimCount; i++) {
    273             CatLog.d(LOG_TAG, "slotId: " + i);
    274             mStkService[i] = CatService.getInstance(i);
    275             mStkContext[i] = new StkContext();
    276             mStkContext[i].mSlotId = i;
    277             mStkContext[i].mCmdsQ = new LinkedList<DelayedCmd>();
    278         }
    279 
    280         Thread serviceThread = new Thread(null, this, "Stk App Service");
    281         serviceThread.start();
    282         mNotificationManager = (NotificationManager) mContext
    283                 .getSystemService(Context.NOTIFICATION_SERVICE);
    284         sInstance = this;
    285     }
    286 
    287     @Override
    288     public void onStart(Intent intent, int startId) {
    289         if (intent == null) {
    290             CatLog.d(LOG_TAG, "StkAppService onStart intent is null so return");
    291             return;
    292         }
    293 
    294         Bundle args = intent.getExtras();
    295         if (args == null) {
    296             CatLog.d(LOG_TAG, "StkAppService onStart args is null so return");
    297             return;
    298         }
    299 
    300         int op = args.getInt(OPCODE);
    301         int slotId = 0;
    302         int i = 0;
    303         if (op != OP_BOOT_COMPLETED) {
    304             slotId = args.getInt(SLOT_ID);
    305         }
    306         CatLog.d(LOG_TAG, "onStart sim id: " + slotId + ", op: " + op + ", *****");
    307         if ((slotId >= 0 && slotId < mSimCount) && mStkService[slotId] == null) {
    308             mStkService[slotId] = CatService.getInstance(slotId);
    309             if (mStkService[slotId] == null) {
    310                 CatLog.d(LOG_TAG, "mStkService is: " + mStkContext[slotId].mStkServiceState);
    311                 mStkContext[slotId].mStkServiceState = STATE_NOT_EXIST;
    312                 //Check other StkService state.
    313                 //If all StkServices are not available, stop itself and uninstall apk.
    314                 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) {
    315                     if (i != slotId
    316                             && (mStkContext[i].mStkServiceState == STATE_UNKNOWN
    317                             || mStkContext[i].mStkServiceState == STATE_EXIST)) {
    318                        break;
    319                    }
    320                 }
    321             } else {
    322                 mStkContext[slotId].mStkServiceState = STATE_EXIST;
    323             }
    324             if (i == mSimCount) {
    325                 stopSelf();
    326                 StkAppInstaller.unInstall(mContext);
    327                 return;
    328             }
    329         }
    330 
    331         waitForLooper();
    332 
    333         Message msg = mServiceHandler.obtainMessage();
    334         msg.arg1 = op;
    335         msg.arg2 = slotId;
    336         switch(msg.arg1) {
    337         case OP_CMD:
    338             msg.obj = args.getParcelable(CMD_MSG);
    339             break;
    340         case OP_RESPONSE:
    341         case OP_CARD_STATUS_CHANGED:
    342         case OP_LOCALE_CHANGED:
    343         case OP_ALPHA_NOTIFY:
    344         case OP_IDLE_SCREEN:
    345             msg.obj = args;
    346             /* falls through */
    347         case OP_LAUNCH_APP:
    348         case OP_END_SESSION:
    349         case OP_BOOT_COMPLETED:
    350             break;
    351         default:
    352             return;
    353         }
    354         mServiceHandler.sendMessage(msg);
    355     }
    356 
    357     @Override
    358     public void onDestroy() {
    359         CatLog.d(LOG_TAG, "onDestroy()");
    360         if (mStkCmdReceiver != null) {
    361             unregisterReceiver(mStkCmdReceiver);
    362             mStkCmdReceiver = null;
    363         }
    364         mPowerManager = null;
    365         waitForLooper();
    366         mServiceLooper.quit();
    367     }
    368 
    369     @Override
    370     public IBinder onBind(Intent intent) {
    371         return null;
    372     }
    373 
    374     public void run() {
    375         Looper.prepare();
    376 
    377         mServiceLooper = Looper.myLooper();
    378         mServiceHandler = new ServiceHandler();
    379 
    380         Looper.loop();
    381     }
    382 
    383     /*
    384      * Package api used by StkMenuActivity to indicate if its on the foreground.
    385      */
    386     void indicateMenuVisibility(boolean visibility, int slotId) {
    387         if (slotId >= 0 && slotId < mSimCount) {
    388             mStkContext[slotId].mMenuIsVisible = visibility;
    389         }
    390     }
    391 
    392     /*
    393      * Package api used by StkDialogActivity to indicate if its on the foreground.
    394      */
    395     void setDisplayTextDlgVisibility(boolean visibility, int slotId) {
    396         if (slotId >= 0 && slotId < mSimCount) {
    397             mStkContext[slotId].mDisplayTextDlgIsVisibile = visibility;
    398         }
    399     }
    400 
    401     boolean isInputPending(int slotId) {
    402         if (slotId >= 0 && slotId < mSimCount) {
    403             CatLog.d(LOG_TAG, "isInputFinishBySrv: " + mStkContext[slotId].mIsInputPending);
    404             return mStkContext[slotId].mIsInputPending;
    405         }
    406         return false;
    407     }
    408 
    409     boolean isMenuPending(int slotId) {
    410         if (slotId >= 0 && slotId < mSimCount) {
    411             CatLog.d(LOG_TAG, "isMenuPending: " + mStkContext[slotId].mIsMenuPending);
    412             return mStkContext[slotId].mIsMenuPending;
    413         }
    414         return false;
    415     }
    416 
    417     boolean isDialogPending(int slotId) {
    418         if (slotId >= 0 && slotId < mSimCount) {
    419             CatLog.d(LOG_TAG, "isDialogPending: " + mStkContext[slotId].mIsDialogPending);
    420             return mStkContext[slotId].mIsDialogPending;
    421         }
    422         return false;
    423     }
    424 
    425     /*
    426      * Package api used by StkMenuActivity to get its Menu parameter.
    427      */
    428     Menu getMenu(int slotId) {
    429         CatLog.d(LOG_TAG, "StkAppService, getMenu, sim id: " + slotId);
    430         if (slotId >=0 && slotId < mSimCount) {
    431             return mStkContext[slotId].mCurrentMenu;
    432         } else {
    433             return null;
    434         }
    435     }
    436 
    437     /*
    438      * Package api used by StkMenuActivity to get its Main Menu parameter.
    439      */
    440     Menu getMainMenu(int slotId) {
    441         CatLog.d(LOG_TAG, "StkAppService, getMainMenu, sim id: " + slotId);
    442         if (slotId >=0 && slotId < mSimCount && (mStkContext[slotId].mMainCmd != null)) {
    443             return mStkContext[slotId].mMainCmd.getMenu();
    444         } else {
    445             return null;
    446         }
    447     }
    448 
    449     /*
    450      * Package api used by UI Activities and Dialogs to communicate directly
    451      * with the service to deliver state information and parameters.
    452      */
    453     static StkAppService getInstance() {
    454         return sInstance;
    455     }
    456 
    457     private void waitForLooper() {
    458         while (mServiceHandler == null) {
    459             synchronized (this) {
    460                 try {
    461                     wait(100);
    462                 } catch (InterruptedException e) {
    463                 }
    464             }
    465         }
    466     }
    467 
    468     private final class ServiceHandler extends Handler {
    469         @Override
    470         public void handleMessage(Message msg) {
    471             if(null == msg) {
    472                 CatLog.d(LOG_TAG, "ServiceHandler handleMessage msg is null");
    473                 return;
    474             }
    475             int opcode = msg.arg1;
    476             int slotId = msg.arg2;
    477 
    478             CatLog.d(LOG_TAG, "handleMessage opcode[" + opcode + "], sim id[" + slotId + "]");
    479             if (opcode == OP_CMD && msg.obj != null &&
    480                     ((CatCmdMessage)msg.obj).getCmdType()!= null) {
    481                 CatLog.d(LOG_TAG, "cmdName[" + ((CatCmdMessage)msg.obj).getCmdType().name() + "]");
    482             }
    483             mStkContext[slotId].mOpCode = opcode;
    484             switch (opcode) {
    485             case OP_LAUNCH_APP:
    486                 if (mStkContext[slotId].mMainCmd == null) {
    487                     CatLog.d(LOG_TAG, "mMainCmd is null");
    488                     // nothing todo when no SET UP MENU command didn't arrive.
    489                     return;
    490                 }
    491                 CatLog.d(LOG_TAG, "handleMessage OP_LAUNCH_APP - mCmdInProgress[" +
    492                         mStkContext[slotId].mCmdInProgress + "]");
    493 
    494                 //If there is a pending activity for the slot id,
    495                 //just finish it and create a new one to handle the pending command.
    496                 cleanUpInstanceStackBySlot(slotId);
    497 
    498                 CatLog.d(LOG_TAG, "Current cmd type: " +
    499                         mStkContext[slotId].mCurrentCmd.getCmdType());
    500                 //Restore the last command from stack by slot id.
    501                 restoreInstanceFromStackBySlot(slotId);
    502                 break;
    503             case OP_CMD:
    504                 CatLog.d(LOG_TAG, "[OP_CMD]");
    505                 CatCmdMessage cmdMsg = (CatCmdMessage) msg.obj;
    506                 // There are two types of commands:
    507                 // 1. Interactive - user's response is required.
    508                 // 2. Informative - display a message, no interaction with the user.
    509                 //
    510                 // Informative commands can be handled immediately without any delay.
    511                 // Interactive commands can't override each other. So if a command
    512                 // is already in progress, we need to queue the next command until
    513                 // the user has responded or a timeout expired.
    514                 if (!isCmdInteractive(cmdMsg)) {
    515                     handleCmd(cmdMsg, slotId);
    516                 } else {
    517                     if (!mStkContext[slotId].mCmdInProgress) {
    518                         mStkContext[slotId].mCmdInProgress = true;
    519                         handleCmd((CatCmdMessage) msg.obj, slotId);
    520                     } else {
    521                         CatLog.d(LOG_TAG, "[Interactive][in progress]");
    522                         mStkContext[slotId].mCmdsQ.addLast(new DelayedCmd(OP_CMD,
    523                                 (CatCmdMessage) msg.obj, slotId));
    524                     }
    525                 }
    526                 break;
    527             case OP_RESPONSE:
    528                 handleCmdResponse((Bundle) msg.obj, slotId);
    529                 // call delayed commands if needed.
    530                 if (mStkContext[slotId].mCmdsQ.size() != 0) {
    531                     callDelayedMsg(slotId);
    532                 } else {
    533                     mStkContext[slotId].mCmdInProgress = false;
    534                 }
    535                 break;
    536             case OP_END_SESSION:
    537                 if (!mStkContext[slotId].mCmdInProgress) {
    538                     mStkContext[slotId].mCmdInProgress = true;
    539                     handleSessionEnd(slotId);
    540                 } else {
    541                     mStkContext[slotId].mCmdsQ.addLast(
    542                             new DelayedCmd(OP_END_SESSION, null, slotId));
    543                 }
    544                 break;
    545             case OP_BOOT_COMPLETED:
    546                 CatLog.d(LOG_TAG, " OP_BOOT_COMPLETED");
    547                 int i = 0;
    548                 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) {
    549                     if (mStkContext[i].mMainCmd != null) {
    550                         break;
    551                     }
    552                 }
    553                 if (i == mSimCount) {
    554                     StkAppInstaller.unInstall(mContext);
    555                 }
    556                 break;
    557             case OP_DELAYED_MSG:
    558                 handleDelayedCmd(slotId);
    559                 break;
    560             case OP_CARD_STATUS_CHANGED:
    561                 CatLog.d(LOG_TAG, "Card/Icc Status change received");
    562                 handleCardStatusChangeAndIccRefresh((Bundle) msg.obj, slotId);
    563                 break;
    564             case OP_SET_ACT_INST:
    565                 Activity act = new Activity();
    566                 act = (Activity) msg.obj;
    567                 CatLog.d(LOG_TAG, "Set activity instance. " + act);
    568                 mStkContext[slotId].mActivityInstance = act;
    569                 break;
    570             case OP_SET_DAL_INST:
    571                 Activity dal = new Activity();
    572                 CatLog.d(LOG_TAG, "Set dialog instance. " + dal);
    573                 dal = (Activity) msg.obj;
    574                 mStkContext[slotId].mDialogInstance = dal;
    575                 break;
    576             case OP_SET_MAINACT_INST:
    577                 Activity mainAct = new Activity();
    578                 mainAct = (Activity) msg.obj;
    579                 CatLog.d(LOG_TAG, "Set activity instance. " + mainAct);
    580                 mStkContext[slotId].mMainActivityInstance = mainAct;
    581                 break;
    582             case OP_LOCALE_CHANGED:
    583                 CatLog.d(this, "Locale Changed");
    584                 for (int slot = PhoneConstants.SIM_ID_1; slot < mSimCount; slot++) {
    585                     checkForSetupEvent(LANGUAGE_SELECTION_EVENT, (Bundle) msg.obj, slot);
    586                 }
    587                 break;
    588             case OP_ALPHA_NOTIFY:
    589                 handleAlphaNotify((Bundle) msg.obj);
    590                 break;
    591             case OP_IDLE_SCREEN:
    592                for (int slot = 0; slot < mSimCount; slot++) {
    593                     if (mStkContext[slot] != null) {
    594                         handleIdleScreen(slot);
    595                     }
    596                 }
    597                 break;
    598             }
    599         }
    600 
    601         private void handleCardStatusChangeAndIccRefresh(Bundle args, int slotId) {
    602             boolean cardStatus = args.getBoolean(AppInterface.CARD_STATUS);
    603 
    604             CatLog.d(LOG_TAG, "CardStatus: " + cardStatus);
    605             if (cardStatus == false) {
    606                 CatLog.d(LOG_TAG, "CARD is ABSENT");
    607                 // Uninstall STKAPP, Clear Idle text, Stop StkAppService
    608                 mNotificationManager.cancel(getNotificationId(slotId));
    609                 if (isAllOtherCardsAbsent(slotId)) {
    610                     CatLog.d(LOG_TAG, "All CARDs are ABSENT");
    611                     StkAppInstaller.unInstall(mContext);
    612                     stopSelf();
    613                 }
    614             } else {
    615                 IccRefreshResponse state = new IccRefreshResponse();
    616                 state.refreshResult = args.getInt(AppInterface.REFRESH_RESULT);
    617 
    618                 CatLog.d(LOG_TAG, "Icc Refresh Result: "+ state.refreshResult);
    619                 if ((state.refreshResult == IccRefreshResponse.REFRESH_RESULT_INIT) ||
    620                     (state.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET)) {
    621                     // Clear Idle Text
    622                     mNotificationManager.cancel(getNotificationId(slotId));
    623                 }
    624 
    625                 if (state.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET) {
    626                     // Uninstall STkmenu
    627                     if (isAllOtherCardsAbsent(slotId)) {
    628                         StkAppInstaller.unInstall(mContext);
    629                     }
    630                     mStkContext[slotId].mCurrentMenu = null;
    631                     mStkContext[slotId].mMainCmd = null;
    632                 }
    633             }
    634         }
    635     }
    636     /*
    637      * Check if all SIMs are absent except the id of slot equals "slotId".
    638      */
    639     private boolean isAllOtherCardsAbsent(int slotId) {
    640         TelephonyManager mTm = (TelephonyManager) mContext.getSystemService(
    641                 Context.TELEPHONY_SERVICE);
    642         int i = 0;
    643 
    644         for (i = 0; i < mSimCount; i++) {
    645             if (i != slotId && mTm.hasIccCard(i)) {
    646                 break;
    647             }
    648         }
    649         if (i == mSimCount) {
    650             return true;
    651         } else {
    652             return false;
    653         }
    654     }
    655 
    656     /*
    657      * If the device is not in an interactive state, we can assume
    658      * that the screen is idle.
    659      */
    660     private boolean isScreenIdle() {
    661         return (!mPowerManager.isInteractive());
    662     }
    663 
    664     private void handleIdleScreen(int slotId) {
    665 
    666         // If the idle screen event is present in the list need to send the
    667         // response to SIM.
    668         CatLog.d(this, "Need to send IDLE SCREEN Available event to SIM");
    669         checkForSetupEvent(IDLE_SCREEN_AVAILABLE_EVENT, null, slotId);
    670 
    671         if (mStkContext[slotId].mIdleModeTextCmd != null) {
    672            launchIdleText(slotId);
    673         }
    674     }
    675 
    676     private void sendScreenBusyResponse(int slotId) {
    677         if (mStkContext[slotId].mCurrentCmd == null) {
    678             return;
    679         }
    680         CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentCmd);
    681         CatLog.d(this, "SCREEN_BUSY");
    682         resMsg.setResultCode(ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS);
    683         mStkService[slotId].onCmdResponse(resMsg);
    684         if (mStkContext[slotId].mCmdsQ.size() != 0) {
    685             callDelayedMsg(slotId);
    686         } else {
    687             mStkContext[slotId].mCmdInProgress = false;
    688         }
    689     }
    690 
    691     private void sendResponse(int resId, int slotId, boolean confirm) {
    692         Message msg = mServiceHandler.obtainMessage();
    693         msg.arg1 = OP_RESPONSE;
    694         msg.arg2 = slotId;
    695         Bundle args = new Bundle();
    696         args.putInt(StkAppService.RES_ID, resId);
    697         args.putBoolean(StkAppService.CONFIRMATION, confirm);
    698         msg.obj = args;
    699         mServiceHandler.sendMessage(msg);
    700     }
    701 
    702     private boolean isCmdInteractive(CatCmdMessage cmd) {
    703         switch (cmd.getCmdType()) {
    704         case SEND_DTMF:
    705         case SEND_SMS:
    706         case SEND_SS:
    707         case SEND_USSD:
    708         case SET_UP_IDLE_MODE_TEXT:
    709         case SET_UP_MENU:
    710         case CLOSE_CHANNEL:
    711         case RECEIVE_DATA:
    712         case SEND_DATA:
    713         case SET_UP_EVENT_LIST:
    714             return false;
    715         }
    716 
    717         return true;
    718     }
    719 
    720     private void handleDelayedCmd(int slotId) {
    721         CatLog.d(LOG_TAG, "handleDelayedCmd, slotId: " + slotId);
    722         if (mStkContext[slotId].mCmdsQ.size() != 0) {
    723             DelayedCmd cmd = mStkContext[slotId].mCmdsQ.poll();
    724             if (cmd != null) {
    725                 CatLog.d(LOG_TAG, "handleDelayedCmd - queue size: " +
    726                         mStkContext[slotId].mCmdsQ.size() +
    727                         " id: " + cmd.id + "sim id: " + cmd.slotId);
    728                 switch (cmd.id) {
    729                 case OP_CMD:
    730                     handleCmd(cmd.msg, cmd.slotId);
    731                     break;
    732                 case OP_END_SESSION:
    733                     handleSessionEnd(cmd.slotId);
    734                     break;
    735                 }
    736             }
    737         }
    738     }
    739 
    740     private void callDelayedMsg(int slotId) {
    741         Message msg = mServiceHandler.obtainMessage();
    742         msg.arg1 = OP_DELAYED_MSG;
    743         msg.arg2 = slotId;
    744         mServiceHandler.sendMessage(msg);
    745     }
    746 
    747     private void callSetActivityInstMsg(int inst_type, int slotId, Object obj) {
    748         Message msg = mServiceHandler.obtainMessage();
    749         msg.obj = obj;
    750         msg.arg1 = inst_type;
    751         msg.arg2 = slotId;
    752         mServiceHandler.sendMessage(msg);
    753     }
    754 
    755     private void handleSessionEnd(int slotId) {
    756         // We should finish all pending activity if receiving END SESSION command.
    757         cleanUpInstanceStackBySlot(slotId);
    758 
    759         mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd;
    760         CatLog.d(LOG_TAG, "[handleSessionEnd] - mCurrentCmd changed to mMainCmd!.");
    761         mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mMainCmd;
    762         CatLog.d(LOG_TAG, "slotId: " + slotId + ", mMenuState: " +
    763                 mStkContext[slotId].mMenuState);
    764 
    765         mStkContext[slotId].mIsInputPending = false;
    766         mStkContext[slotId].mIsMenuPending = false;
    767         mStkContext[slotId].mIsDialogPending = false;
    768 
    769         if (mStkContext[slotId].mMainCmd == null) {
    770             CatLog.d(LOG_TAG, "[handleSessionEnd][mMainCmd is null!]");
    771         }
    772         mStkContext[slotId].lastSelectedItem = null;
    773         // In case of SET UP MENU command which removed the app, don't
    774         // update the current menu member.
    775         if (mStkContext[slotId].mCurrentMenu != null && mStkContext[slotId].mMainCmd != null) {
    776             mStkContext[slotId].mCurrentMenu = mStkContext[slotId].mMainCmd.getMenu();
    777         }
    778         CatLog.d(LOG_TAG, "[handleSessionEnd][mMenuState]" + mStkContext[slotId].mMenuIsVisible);
    779         // In mutiple instance architecture, the main menu for slotId will be finished when user
    780         // goes to the Stk menu of the other SIM. So, we should launch a new instance for the
    781         // main menu if the main menu instance has been finished.
    782         // If the current menu is secondary menu, we should launch main menu.
    783         if (StkMenuActivity.STATE_SECONDARY == mStkContext[slotId].mMenuState) {
    784             launchMenuActivity(null, slotId);
    785         }
    786         if (mStkContext[slotId].mCmdsQ.size() != 0) {
    787             callDelayedMsg(slotId);
    788         } else {
    789             mStkContext[slotId].mCmdInProgress = false;
    790         }
    791         // In case a launch browser command was just confirmed, launch that url.
    792         if (mStkContext[slotId].launchBrowser) {
    793             mStkContext[slotId].launchBrowser = false;
    794             launchBrowser(mStkContext[slotId].mBrowserSettings);
    795         }
    796     }
    797 
    798     // returns true if any Stk related activity already has focus on the screen
    799     private boolean isTopOfStack() {
    800         ActivityManager mAcivityManager = (ActivityManager) mContext
    801                 .getSystemService(ACTIVITY_SERVICE);
    802         String currentPackageName = mAcivityManager.getRunningTasks(1).get(0).topActivity
    803                 .getPackageName();
    804         if (null != currentPackageName) {
    805             return currentPackageName.equals(PACKAGE_NAME);
    806         }
    807 
    808         return false;
    809     }
    810 
    811     private void handleCmd(CatCmdMessage cmdMsg, int slotId) {
    812 
    813         if (cmdMsg == null) {
    814             return;
    815         }
    816         // save local reference for state tracking.
    817         mStkContext[slotId].mCurrentCmd = cmdMsg;
    818         boolean waitForUsersResponse = true;
    819 
    820         mStkContext[slotId].mIsInputPending = false;
    821         mStkContext[slotId].mIsMenuPending = false;
    822         mStkContext[slotId].mIsDialogPending = false;
    823 
    824         CatLog.d(LOG_TAG,"[handleCmd]" + cmdMsg.getCmdType().name());
    825         switch (cmdMsg.getCmdType()) {
    826         case DISPLAY_TEXT:
    827             TextMessage msg = cmdMsg.geTextMessage();
    828             waitForUsersResponse = msg.responseNeeded;
    829             if (mStkContext[slotId].lastSelectedItem != null) {
    830                 msg.title = mStkContext[slotId].lastSelectedItem;
    831             } else if (mStkContext[slotId].mMainCmd != null){
    832                 msg.title = mStkContext[slotId].mMainCmd.getMenu().title;
    833             } else {
    834                 // TODO: get the carrier name from the SIM
    835                 msg.title = "";
    836             }
    837             //If we receive a low priority Display Text and the device is
    838             // not displaying any STK related activity and the screen is not idle
    839             // ( that is, device is in an interactive state), then send a screen busy
    840             // terminal response. Otherwise display the message. The existing
    841             // displayed message shall be updated with the new display text
    842             // proactive command (Refer to ETSI TS 102 384 section 27.22.4.1.4.4.2).
    843             if (!(msg.isHighPriority || mStkContext[slotId].mMenuIsVisible
    844                     || mStkContext[slotId].mDisplayTextDlgIsVisibile || isTopOfStack())) {
    845                 if(!isScreenIdle()) {
    846                     CatLog.d(LOG_TAG, "Screen is not idle");
    847                     sendScreenBusyResponse(slotId);
    848                 } else {
    849                     launchTextDialog(slotId);
    850                 }
    851             } else {
    852                 launchTextDialog(slotId);
    853             }
    854             break;
    855         case SELECT_ITEM:
    856             CatLog.d(LOG_TAG, "SELECT_ITEM +");
    857             mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mCurrentCmd;
    858             mStkContext[slotId].mCurrentMenu = cmdMsg.getMenu();
    859             launchMenuActivity(cmdMsg.getMenu(), slotId);
    860             break;
    861         case SET_UP_MENU:
    862             mStkContext[slotId].mCmdInProgress = false;
    863             mStkContext[slotId].mMainCmd = mStkContext[slotId].mCurrentCmd;
    864             mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mCurrentCmd;
    865             mStkContext[slotId].mCurrentMenu = cmdMsg.getMenu();
    866             CatLog.d(LOG_TAG, "SET_UP_MENU [" + removeMenu(slotId) + "]");
    867 
    868             if (removeMenu(slotId)) {
    869                 int i = 0;
    870                 CatLog.d(LOG_TAG, "removeMenu() - Uninstall App");
    871                 mStkContext[slotId].mCurrentMenu = null;
    872                 mStkContext[slotId].mMainCmd = null;
    873                 //Check other setup menu state. If all setup menu are removed, uninstall apk.
    874                 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) {
    875                     if (i != slotId
    876                             && (mStkContext[slotId].mSetupMenuState == STATE_UNKNOWN
    877                             || mStkContext[slotId].mSetupMenuState == STATE_EXIST)) {
    878                         CatLog.d(LOG_TAG, "Not Uninstall App:" + i + ","
    879                                 + mStkContext[slotId].mSetupMenuState);
    880                         break;
    881                     }
    882                 }
    883                 if (i == mSimCount) {
    884                     StkAppInstaller.unInstall(mContext);
    885                 }
    886             } else {
    887                 CatLog.d(LOG_TAG, "install App");
    888                 StkAppInstaller.install(mContext);
    889             }
    890             if (mStkContext[slotId].mMenuIsVisible) {
    891                 launchMenuActivity(null, slotId);
    892             }
    893             break;
    894         case GET_INPUT:
    895         case GET_INKEY:
    896             launchInputActivity(slotId);
    897             break;
    898         case SET_UP_IDLE_MODE_TEXT:
    899             waitForUsersResponse = false;
    900             mStkContext[slotId].mIdleModeTextCmd = mStkContext[slotId].mCurrentCmd;
    901             TextMessage idleModeText = mStkContext[slotId].mCurrentCmd.geTextMessage();
    902             if (idleModeText == null) {
    903                 launchIdleText(slotId);
    904                 mStkContext[slotId].mIdleModeTextCmd = null;
    905             }
    906             mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd;
    907             if ((mStkContext[slotId].mIdleModeTextCmd != null) && isScreenIdle()) {
    908                 CatLog.d(this, "set up idle mode");
    909                 launchIdleText(slotId);
    910             }
    911             break;
    912         case SEND_DTMF:
    913         case SEND_SMS:
    914         case SEND_SS:
    915         case SEND_USSD:
    916         case GET_CHANNEL_STATUS:
    917             waitForUsersResponse = false;
    918             launchEventMessage(slotId);
    919             break;
    920         case LAUNCH_BROWSER:
    921             TextMessage alphaId = mStkContext[slotId].mCurrentCmd.geTextMessage();
    922             if ((mStkContext[slotId].mCurrentCmd.getBrowserSettings().mode
    923                     == LaunchBrowserMode.LAUNCH_IF_NOT_ALREADY_LAUNCHED) &&
    924                     ((alphaId == null) || TextUtils.isEmpty(alphaId.text))) {
    925                 // don't need user confirmation in this case
    926                 // just launch the browser or spawn a new tab
    927                 CatLog.d(this, "Browser mode is: launch if not already launched " +
    928                         "and user confirmation is not currently needed.\n" +
    929                         "supressing confirmation dialogue and confirming silently...");
    930                 mStkContext[slotId].launchBrowser = true;
    931                 mStkContext[slotId].mBrowserSettings =
    932                         mStkContext[slotId].mCurrentCmd.getBrowserSettings();
    933                 sendResponse(RES_ID_CONFIRM, slotId, true);
    934             } else {
    935                 launchConfirmationDialog(alphaId, slotId);
    936             }
    937             break;
    938         case SET_UP_CALL:
    939             TextMessage mesg = mStkContext[slotId].mCurrentCmd.getCallSettings().confirmMsg;
    940             if((mesg != null) && (mesg.text == null || mesg.text.length() == 0)) {
    941                 mesg.text = getResources().getString(R.string.default_setup_call_msg);
    942             }
    943             CatLog.d(this, "SET_UP_CALL mesg.text " + mesg.text);
    944             launchConfirmationDialog(mesg, slotId);
    945             break;
    946         case PLAY_TONE:
    947             launchToneDialog(slotId);
    948             break;
    949         case OPEN_CHANNEL:
    950             launchOpenChannelDialog(slotId);
    951             break;
    952         case CLOSE_CHANNEL:
    953         case RECEIVE_DATA:
    954         case SEND_DATA:
    955             TextMessage m = mStkContext[slotId].mCurrentCmd.geTextMessage();
    956 
    957             if ((m != null) && (m.text == null)) {
    958                 switch(cmdMsg.getCmdType()) {
    959                 case CLOSE_CHANNEL:
    960                     m.text = getResources().getString(R.string.default_close_channel_msg);
    961                     break;
    962                 case RECEIVE_DATA:
    963                     m.text = getResources().getString(R.string.default_receive_data_msg);
    964                     break;
    965                 case SEND_DATA:
    966                     m.text = getResources().getString(R.string.default_send_data_msg);
    967                     break;
    968                 }
    969             }
    970             /*
    971              * Display indication in the form of a toast to the user if required.
    972              */
    973             launchEventMessage(slotId);
    974             break;
    975         case SET_UP_EVENT_LIST:
    976             mStkContext[slotId].mSetupEventListSettings =
    977                     mStkContext[slotId].mCurrentCmd.getSetEventList();
    978             mStkContext[slotId].mCurrentSetupEventCmd = mStkContext[slotId].mCurrentCmd;
    979             mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd;
    980             if (isScreenIdle()) {
    981                 CatLog.d(this," Check if IDLE_SCREEN_AVAILABLE_EVENT is present in List");
    982                 checkForSetupEvent(IDLE_SCREEN_AVAILABLE_EVENT, null, slotId);
    983             }
    984             break;
    985         }
    986 
    987         if (!waitForUsersResponse) {
    988             if (mStkContext[slotId].mCmdsQ.size() != 0) {
    989                 callDelayedMsg(slotId);
    990             } else {
    991                 mStkContext[slotId].mCmdInProgress = false;
    992             }
    993         }
    994     }
    995 
    996     private void handleCmdResponse(Bundle args, int slotId) {
    997         CatLog.d(LOG_TAG, "handleCmdResponse, sim id: " + slotId);
    998         if (mStkContext[slotId].mCurrentCmd == null) {
    999             return;
   1000         }
   1001 
   1002         if (mStkService[slotId] == null) {
   1003             mStkService[slotId] = CatService.getInstance(slotId);
   1004             if (mStkService[slotId] == null) {
   1005                 // This should never happen (we should be responding only to a message
   1006                 // that arrived from StkService). It has to exist by this time
   1007                 CatLog.d(LOG_TAG, "Exception! mStkService is null when we need to send response.");
   1008                 throw new RuntimeException("mStkService is null when we need to send response");
   1009             }
   1010         }
   1011 
   1012         CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentCmd);
   1013 
   1014         // set result code
   1015         boolean helpRequired = args.getBoolean(HELP, false);
   1016         boolean confirmed    = false;
   1017 
   1018         switch(args.getInt(RES_ID)) {
   1019         case RES_ID_MENU_SELECTION:
   1020             CatLog.d(LOG_TAG, "MENU_SELECTION=" + mStkContext[slotId].
   1021                     mCurrentMenuCmd.getCmdType());
   1022             int menuSelection = args.getInt(MENU_SELECTION);
   1023             switch(mStkContext[slotId].mCurrentMenuCmd.getCmdType()) {
   1024             case SET_UP_MENU:
   1025             case SELECT_ITEM:
   1026                 mStkContext[slotId].lastSelectedItem = getItemName(menuSelection, slotId);
   1027                 if (helpRequired) {
   1028                     resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
   1029                 } else {
   1030                     resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ?
   1031                             ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK);
   1032                 }
   1033                 resMsg.setMenuSelection(menuSelection);
   1034                 break;
   1035             }
   1036             break;
   1037         case RES_ID_INPUT:
   1038             CatLog.d(LOG_TAG, "RES_ID_INPUT");
   1039             String input = args.getString(INPUT);
   1040             if (input != null && (null != mStkContext[slotId].mCurrentCmd.geInput()) &&
   1041                     (mStkContext[slotId].mCurrentCmd.geInput().yesNo)) {
   1042                 boolean yesNoSelection = input
   1043                         .equals(StkInputActivity.YES_STR_RESPONSE);
   1044                 resMsg.setYesNo(yesNoSelection);
   1045             } else {
   1046                 if (helpRequired) {
   1047                     resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
   1048                 } else {
   1049                     resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ?
   1050                             ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK);
   1051                     resMsg.setInput(input);
   1052                 }
   1053             }
   1054             break;
   1055         case RES_ID_CONFIRM:
   1056             CatLog.d(this, "RES_ID_CONFIRM");
   1057             confirmed = args.getBoolean(CONFIRMATION);
   1058             switch (mStkContext[slotId].mCurrentCmd.getCmdType()) {
   1059             case DISPLAY_TEXT:
   1060                 if (confirmed) {
   1061                     resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ?
   1062                             ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK);
   1063                 } else {
   1064                     resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER);
   1065                 }
   1066                 break;
   1067             case LAUNCH_BROWSER:
   1068                 resMsg.setResultCode(confirmed ? ResultCode.OK
   1069                         : ResultCode.UICC_SESSION_TERM_BY_USER);
   1070                 if (confirmed) {
   1071                     mStkContext[slotId].launchBrowser = true;
   1072                     mStkContext[slotId].mBrowserSettings =
   1073                             mStkContext[slotId].mCurrentCmd.getBrowserSettings();
   1074                 }
   1075                 break;
   1076             case SET_UP_CALL:
   1077                 resMsg.setResultCode(ResultCode.OK);
   1078                 resMsg.setConfirmation(confirmed);
   1079                 if (confirmed) {
   1080                     CatLog.d(this, "Going back to mainMenu before starting a call.");
   1081                     launchMenuActivity(null, slotId);
   1082                     launchEventMessage(slotId,
   1083                             mStkContext[slotId].mCurrentCmd.getCallSettings().callMsg);
   1084                 }
   1085                 break;
   1086             }
   1087             break;
   1088         case RES_ID_DONE:
   1089             resMsg.setResultCode(ResultCode.OK);
   1090             break;
   1091         case RES_ID_BACKWARD:
   1092             CatLog.d(LOG_TAG, "RES_ID_BACKWARD");
   1093             resMsg.setResultCode(ResultCode.BACKWARD_MOVE_BY_USER);
   1094             break;
   1095         case RES_ID_END_SESSION:
   1096             CatLog.d(LOG_TAG, "RES_ID_END_SESSION");
   1097             resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER);
   1098             break;
   1099         case RES_ID_TIMEOUT:
   1100             CatLog.d(LOG_TAG, "RES_ID_TIMEOUT");
   1101             // GCF test-case 27.22.4.1.1 Expected Sequence 1.5 (DISPLAY TEXT,
   1102             // Clear message after delay, successful) expects result code OK.
   1103             // If the command qualifier specifies no user response is required
   1104             // then send OK instead of NO_RESPONSE_FROM_USER
   1105             if ((mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1106                     AppInterface.CommandType.DISPLAY_TEXT.value())
   1107                     && (mStkContext[slotId].mCurrentCmd.geTextMessage().userClear == false)) {
   1108                 resMsg.setResultCode(ResultCode.OK);
   1109             } else {
   1110                 resMsg.setResultCode(ResultCode.NO_RESPONSE_FROM_USER);
   1111             }
   1112             break;
   1113         case RES_ID_CHOICE:
   1114             int choice = args.getInt(CHOICE);
   1115             CatLog.d(this, "User Choice=" + choice);
   1116             switch (choice) {
   1117                 case YES:
   1118                     resMsg.setResultCode(ResultCode.OK);
   1119                     confirmed = true;
   1120                     break;
   1121                 case NO:
   1122                     resMsg.setResultCode(ResultCode.USER_NOT_ACCEPT);
   1123                     break;
   1124             }
   1125 
   1126             if (mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1127                     AppInterface.CommandType.OPEN_CHANNEL.value()) {
   1128                 resMsg.setConfirmation(confirmed);
   1129             }
   1130             break;
   1131 
   1132         default:
   1133             CatLog.d(LOG_TAG, "Unknown result id");
   1134             return;
   1135         }
   1136 
   1137         if (null != mStkContext[slotId].mCurrentCmd &&
   1138                 null != mStkContext[slotId].mCurrentCmd.getCmdType()) {
   1139             CatLog.d(LOG_TAG, "handleCmdResponse- cmdName[" +
   1140                     mStkContext[slotId].mCurrentCmd.getCmdType().name() + "]");
   1141         }
   1142         mStkService[slotId].onCmdResponse(resMsg);
   1143     }
   1144 
   1145     /**
   1146      * Returns 0 or FLAG_ACTIVITY_NO_USER_ACTION, 0 means the user initiated the action.
   1147      *
   1148      * @param userAction If the userAction is yes then we always return 0 otherwise
   1149      * mMenuIsVisible is used to determine what to return. If mMenuIsVisible is true
   1150      * then we are the foreground app and we'll return 0 as from our perspective a
   1151      * user action did cause. If it's false than we aren't the foreground app and
   1152      * FLAG_ACTIVITY_NO_USER_ACTION is returned.
   1153      *
   1154      * @return 0 or FLAG_ACTIVITY_NO_USER_ACTION
   1155      */
   1156     private int getFlagActivityNoUserAction(InitiatedByUserAction userAction, int slotId) {
   1157         return ((userAction == InitiatedByUserAction.yes) | mStkContext[slotId].mMenuIsVisible)
   1158                 ? 0 : Intent.FLAG_ACTIVITY_NO_USER_ACTION;
   1159     }
   1160     /**
   1161      * This method is used for cleaning up pending instances in stack.
   1162      */
   1163     private void cleanUpInstanceStackBySlot(int slotId) {
   1164         Activity activity = mStkContext[slotId].getPendingActivityInstance();
   1165         Activity dialog = mStkContext[slotId].getPendingDialogInstance();
   1166         CatLog.d(LOG_TAG, "cleanUpInstanceStackBySlot slotId: " + slotId);
   1167         if (mStkContext[slotId].mCurrentCmd == null) {
   1168             CatLog.d(LOG_TAG, "current cmd is null.");
   1169             return;
   1170         }
   1171         if (activity != null) {
   1172             CatLog.d(LOG_TAG, "current cmd type: " +
   1173                     mStkContext[slotId].mCurrentCmd.getCmdType());
   1174             if (mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1175                     AppInterface.CommandType.GET_INPUT.value() ||
   1176                     mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1177                     AppInterface.CommandType.GET_INKEY.value()) {
   1178                 mStkContext[slotId].mIsInputPending = true;
   1179             } else if (mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1180                     AppInterface.CommandType.SET_UP_MENU.value() ||
   1181                     mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1182                     AppInterface.CommandType.SELECT_ITEM.value()) {
   1183                 mStkContext[slotId].mIsMenuPending = true;
   1184             } else {
   1185             }
   1186             CatLog.d(LOG_TAG, "finish pending activity.");
   1187             activity.finish();
   1188             mStkContext[slotId].mActivityInstance = null;
   1189         }
   1190         if (dialog != null) {
   1191             CatLog.d(LOG_TAG, "finish pending dialog.");
   1192             mStkContext[slotId].mIsDialogPending = true;
   1193             dialog.finish();
   1194             mStkContext[slotId].mDialogInstance = null;
   1195         }
   1196     }
   1197     /**
   1198      * This method is used for restoring pending instances from stack.
   1199      */
   1200     private void restoreInstanceFromStackBySlot(int slotId) {
   1201         AppInterface.CommandType cmdType = mStkContext[slotId].mCurrentCmd.getCmdType();
   1202 
   1203         CatLog.d(LOG_TAG, "restoreInstanceFromStackBySlot cmdType : " + cmdType);
   1204         switch(cmdType) {
   1205             case GET_INPUT:
   1206             case GET_INKEY:
   1207                 launchInputActivity(slotId);
   1208                 //Set mMenuIsVisible to true for showing main menu for
   1209                 //following session end command.
   1210                 mStkContext[slotId].mMenuIsVisible = true;
   1211             break;
   1212             case DISPLAY_TEXT:
   1213                 launchTextDialog(slotId);
   1214             break;
   1215             case LAUNCH_BROWSER:
   1216                 launchConfirmationDialog(mStkContext[slotId].mCurrentCmd.geTextMessage(),
   1217                         slotId);
   1218             break;
   1219             case OPEN_CHANNEL:
   1220                 launchOpenChannelDialog(slotId);
   1221             break;
   1222             case SET_UP_CALL:
   1223                 launchConfirmationDialog(mStkContext[slotId].mCurrentCmd.getCallSettings().
   1224                         confirmMsg, slotId);
   1225             break;
   1226             case SET_UP_MENU:
   1227             case SELECT_ITEM:
   1228                 launchMenuActivity(null, slotId);
   1229             break;
   1230         default:
   1231             break;
   1232         }
   1233     }
   1234 
   1235     private void launchMenuActivity(Menu menu, int slotId) {
   1236         Intent newIntent = new Intent(Intent.ACTION_VIEW);
   1237         String targetActivity = STK_MENU_ACTIVITY_NAME;
   1238         String uriString = STK_MENU_URI + System.currentTimeMillis();
   1239         //Set unique URI to create a new instance of activity for different slotId.
   1240         Uri uriData = Uri.parse(uriString);
   1241 
   1242         CatLog.d(LOG_TAG, "launchMenuActivity, slotId: " + slotId + " , " +
   1243                 uriData.toString() + " , " + mStkContext[slotId].mOpCode + ", "
   1244                 + mStkContext[slotId].mMenuState);
   1245         newIntent.setClassName(PACKAGE_NAME, targetActivity);
   1246         int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK;
   1247 
   1248         if (menu == null) {
   1249             // We assume this was initiated by the user pressing the tool kit icon
   1250             intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.yes, slotId);
   1251             if (mStkContext[slotId].mOpCode == OP_END_SESSION) {
   1252                 CatLog.d(LOG_TAG, "launchMenuActivity, return OP_END_SESSION");
   1253                 mStkContext[slotId].mMenuState = StkMenuActivity.STATE_MAIN;
   1254                 if (mStkContext[slotId].mMainActivityInstance != null) {
   1255                     CatLog.d(LOG_TAG, "launchMenuActivity, mMainActivityInstance is not null");
   1256                     return;
   1257                 }
   1258             }
   1259 
   1260             //If the last pending menu is secondary menu, "STATE" should be "STATE_SECONDARY".
   1261             //Otherwise, it should be "STATE_MAIN".
   1262             if (mStkContext[slotId].mOpCode == OP_LAUNCH_APP &&
   1263                     mStkContext[slotId].mMenuState == StkMenuActivity.STATE_SECONDARY) {
   1264                 newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY);
   1265             } else {
   1266                 newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN);
   1267                 mStkContext[slotId].mMenuState = StkMenuActivity.STATE_MAIN;
   1268             }
   1269         } else {
   1270             // We don't know and we'll let getFlagActivityNoUserAction decide.
   1271             intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId);
   1272             newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY);
   1273             mStkContext[slotId].mMenuState = StkMenuActivity.STATE_SECONDARY;
   1274         }
   1275         newIntent.putExtra(SLOT_ID, slotId);
   1276         newIntent.setData(uriData);
   1277         newIntent.setFlags(intentFlags);
   1278         mContext.startActivity(newIntent);
   1279     }
   1280 
   1281     private void launchInputActivity(int slotId) {
   1282         Intent newIntent = new Intent(Intent.ACTION_VIEW);
   1283         String targetActivity = STK_INPUT_ACTIVITY_NAME;
   1284         String uriString = STK_INPUT_URI + System.currentTimeMillis();
   1285         //Set unique URI to create a new instance of activity for different slotId.
   1286         Uri uriData = Uri.parse(uriString);
   1287 
   1288         CatLog.d(LOG_TAG, "launchInputActivity, slotId: " + slotId);
   1289         newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1290                             | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId));
   1291         newIntent.setClassName(PACKAGE_NAME, targetActivity);
   1292         newIntent.putExtra("INPUT", mStkContext[slotId].mCurrentCmd.geInput());
   1293         newIntent.putExtra(SLOT_ID, slotId);
   1294         newIntent.setData(uriData);
   1295         mContext.startActivity(newIntent);
   1296     }
   1297 
   1298     private void launchTextDialog(int slotId) {
   1299         CatLog.d(LOG_TAG, "launchTextDialog, slotId: " + slotId);
   1300         Intent newIntent = new Intent();
   1301         String targetActivity = STK_DIALOG_ACTIVITY_NAME;
   1302         int action = getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId);
   1303         String uriString = STK_DIALOG_URI + System.currentTimeMillis();
   1304         //Set unique URI to create a new instance of activity for different slotId.
   1305         Uri uriData = Uri.parse(uriString);
   1306         if (newIntent != null) {
   1307             newIntent.setClassName(PACKAGE_NAME, targetActivity);
   1308             newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1309                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
   1310                     | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId));
   1311             newIntent.setData(uriData);
   1312             newIntent.putExtra("TEXT", mStkContext[slotId].mCurrentCmd.geTextMessage());
   1313             newIntent.putExtra(SLOT_ID, slotId);
   1314             startActivity(newIntent);
   1315             // For display texts with immediate response, send the terminal response
   1316             // immediately. responseNeeded will be false, if display text command has
   1317             // the immediate response tlv.
   1318             if (!mStkContext[slotId].mCurrentCmd.geTextMessage().responseNeeded) {
   1319                 sendResponse(RES_ID_CONFIRM, slotId, true);
   1320             }
   1321         }
   1322     }
   1323 
   1324     public boolean isStkDialogActivated(Context context) {
   1325         String stkDialogActivity = "com.android.stk.StkDialogActivity";
   1326         boolean activated = false;
   1327         final ActivityManager am = (ActivityManager) context.getSystemService(
   1328                 Context.ACTIVITY_SERVICE);
   1329         String topActivity = am.getRunningTasks(1).get(0).topActivity.getClassName();
   1330 
   1331         CatLog.d(LOG_TAG, "isStkDialogActivated: " + topActivity);
   1332         if (topActivity.equals(stkDialogActivity)) {
   1333             activated = true;
   1334         }
   1335         CatLog.d(LOG_TAG, "activated : " + activated);
   1336         return activated;
   1337     }
   1338 
   1339     private void sendSetUpEventResponse(int event, byte[] addedInfo, int slotId) {
   1340         CatLog.d(this, "sendSetUpEventResponse: event : " + event + "slotId = " + slotId);
   1341 
   1342         if (mStkContext[slotId].mCurrentSetupEventCmd == null){
   1343             CatLog.e(this, "mCurrentSetupEventCmd is null");
   1344             return;
   1345         }
   1346 
   1347         CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentSetupEventCmd);
   1348 
   1349         resMsg.setResultCode(ResultCode.OK);
   1350         resMsg.setEventDownload(event, addedInfo);
   1351 
   1352         mStkService[slotId].onCmdResponse(resMsg);
   1353     }
   1354 
   1355     private void checkForSetupEvent(int event, Bundle args, int slotId) {
   1356         boolean eventPresent = false;
   1357         byte[] addedInfo = null;
   1358         CatLog.d(this, "Event :" + event);
   1359 
   1360         if (mStkContext[slotId].mSetupEventListSettings != null) {
   1361             /* Checks if the event is present in the EventList updated by last
   1362              * SetupEventList Proactive Command */
   1363             for (int i : mStkContext[slotId].mSetupEventListSettings.eventList) {
   1364                  if (event == i) {
   1365                      eventPresent =  true;
   1366                      break;
   1367                  }
   1368             }
   1369 
   1370             /* If Event is present send the response to ICC */
   1371             if (eventPresent == true) {
   1372                 CatLog.d(this, " Event " + event + "exists in the EventList");
   1373 
   1374                 switch (event) {
   1375                     case IDLE_SCREEN_AVAILABLE_EVENT:
   1376                         sendSetUpEventResponse(event, addedInfo, slotId);
   1377                         removeSetUpEvent(event, slotId);
   1378                         break;
   1379                     case LANGUAGE_SELECTION_EVENT:
   1380                         String language =  mContext
   1381                                 .getResources().getConfiguration().locale.getLanguage();
   1382                         CatLog.d(this, "language: " + language);
   1383                         // Each language code is a pair of alpha-numeric characters.
   1384                         // Each alpha-numeric character shall be coded on one byte
   1385                         // using the SMS default 7-bit coded alphabet
   1386                         addedInfo = GsmAlphabet.stringToGsm8BitPacked(language);
   1387                         sendSetUpEventResponse(event, addedInfo, slotId);
   1388                         break;
   1389                     default:
   1390                         break;
   1391                 }
   1392             } else {
   1393                 CatLog.e(this, " Event does not exist in the EventList");
   1394             }
   1395         } else {
   1396             CatLog.e(this, "SetupEventList is not received. Ignoring the event: " + event);
   1397         }
   1398     }
   1399 
   1400     private void  removeSetUpEvent(int event, int slotId) {
   1401         CatLog.d(this, "Remove Event :" + event);
   1402 
   1403         if (mStkContext[slotId].mSetupEventListSettings != null) {
   1404             /*
   1405              * Make new  Eventlist without the event
   1406              */
   1407             for (int i = 0; i < mStkContext[slotId].mSetupEventListSettings.eventList.length; i++) {
   1408                 if (event == mStkContext[slotId].mSetupEventListSettings.eventList[i]) {
   1409                     mStkContext[slotId].mSetupEventListSettings.eventList[i] = INVALID_SETUP_EVENT;
   1410                     break;
   1411                 }
   1412             }
   1413         }
   1414     }
   1415 
   1416     private void launchEventMessage(int slotId) {
   1417         launchEventMessage(slotId, mStkContext[slotId].mCurrentCmd.geTextMessage());
   1418     }
   1419 
   1420     private void launchEventMessage(int slotId, TextMessage msg) {
   1421         if (msg == null || (msg.text != null && msg.text.length() == 0)) {
   1422             CatLog.d(LOG_TAG, "launchEventMessage return");
   1423             return;
   1424         }
   1425 
   1426         Toast toast = new Toast(mContext.getApplicationContext());
   1427         LayoutInflater inflate = (LayoutInflater) mContext
   1428                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   1429         View v = inflate.inflate(R.layout.stk_event_msg, null);
   1430         TextView tv = (TextView) v
   1431                 .findViewById(com.android.internal.R.id.message);
   1432         ImageView iv = (ImageView) v
   1433                 .findViewById(com.android.internal.R.id.icon);
   1434         if (msg.icon != null) {
   1435             iv.setImageBitmap(msg.icon);
   1436         } else {
   1437             iv.setVisibility(View.GONE);
   1438         }
   1439         /* In case of 'self explanatory' stkapp should display the specified
   1440          * icon in proactive command (but not the alpha string).
   1441          * If icon is non-self explanatory and if the icon could not be displayed
   1442          * then alpha string or text data should be displayed
   1443          * Ref: ETSI 102.223,section 6.5.4
   1444          */
   1445         if (mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ||
   1446                 msg.icon == null || !msg.iconSelfExplanatory) {
   1447             tv.setText(msg.text);
   1448         }
   1449 
   1450         toast.setView(v);
   1451         toast.setDuration(Toast.LENGTH_LONG);
   1452         toast.setGravity(Gravity.BOTTOM, 0, 0);
   1453         toast.show();
   1454     }
   1455 
   1456     private void launchConfirmationDialog(TextMessage msg, int slotId) {
   1457         msg.title = mStkContext[slotId].lastSelectedItem;
   1458         Intent newIntent = new Intent();
   1459         String targetActivity = STK_DIALOG_ACTIVITY_NAME;
   1460         String uriString = STK_DIALOG_URI + System.currentTimeMillis();
   1461         //Set unique URI to create a new instance of activity for different slotId.
   1462         Uri uriData = Uri.parse(uriString);
   1463 
   1464         if (newIntent != null) {
   1465             newIntent.setClassName(this, targetActivity);
   1466             newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1467                     | Intent.FLAG_ACTIVITY_NO_HISTORY
   1468                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
   1469                     | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId));
   1470             newIntent.putExtra("TEXT", msg);
   1471             newIntent.putExtra(SLOT_ID, slotId);
   1472             newIntent.setData(uriData);
   1473             startActivity(newIntent);
   1474         }
   1475     }
   1476 
   1477     private void launchBrowser(BrowserSettings settings) {
   1478         if (settings == null) {
   1479             return;
   1480         }
   1481 
   1482         Uri data = null;
   1483         String url;
   1484         if (settings.url == null) {
   1485             // if the command did not contain a URL,
   1486             // launch the browser to the default homepage.
   1487             CatLog.d(this, "no url data provided by proactive command." +
   1488                        " launching browser with stk default URL ... ");
   1489             url = SystemProperties.get(STK_BROWSER_DEFAULT_URL_SYSPROP,
   1490                     "http://www.google.com");
   1491         } else {
   1492             CatLog.d(this, "launch browser command has attached url = " + settings.url);
   1493             url = settings.url;
   1494         }
   1495 
   1496         if (url.startsWith("http://") || url.startsWith("https://")) {
   1497             data = Uri.parse(url);
   1498             CatLog.d(this, "launching browser with url = " + url);
   1499         } else {
   1500             String modifiedUrl = "http://" + url;
   1501             data = Uri.parse(modifiedUrl);
   1502             CatLog.d(this, "launching browser with modified url = " + modifiedUrl);
   1503         }
   1504 
   1505         Intent intent = new Intent(Intent.ACTION_VIEW);
   1506         intent.setData(data);
   1507         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1508         switch (settings.mode) {
   1509         case USE_EXISTING_BROWSER:
   1510             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
   1511             break;
   1512         case LAUNCH_NEW_BROWSER:
   1513             intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
   1514             break;
   1515         case LAUNCH_IF_NOT_ALREADY_LAUNCHED:
   1516             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
   1517             break;
   1518         }
   1519         // start browser activity
   1520         startActivity(intent);
   1521         // a small delay, let the browser start, before processing the next command.
   1522         // this is good for scenarios where a related DISPLAY TEXT command is
   1523         // followed immediately.
   1524         try {
   1525             Thread.sleep(10000);
   1526         } catch (InterruptedException e) {}
   1527     }
   1528 
   1529     private void launchIdleText(int slotId) {
   1530         TextMessage msg = mStkContext[slotId].mIdleModeTextCmd.geTextMessage();
   1531 
   1532         if (msg == null || msg.text ==null) {
   1533             CatLog.d(LOG_TAG,  msg == null ? "mCurrent.getTextMessage is NULL"
   1534                     : "mCurrent.getTextMessage.text is NULL");
   1535             mNotificationManager.cancel(getNotificationId(slotId));
   1536             return;
   1537         } else {
   1538             CatLog.d(LOG_TAG, "launchIdleText - text[" + msg.text
   1539                     + "] iconSelfExplanatory[" + msg.iconSelfExplanatory
   1540                     + "] icon[" + msg.icon + "], sim id: " + slotId);
   1541             CatLog.d(LOG_TAG, "Add IdleMode text");
   1542             PendingIntent pendingIntent = PendingIntent.getService(mContext, 0,
   1543                     new Intent(mContext, StkAppService.class), 0);
   1544 
   1545             final Notification.Builder notificationBuilder = new Notification.Builder(
   1546                     StkAppService.this);
   1547             if (mStkContext[slotId].mMainCmd != null &&
   1548                     mStkContext[slotId].mMainCmd.getMenu() != null) {
   1549                 notificationBuilder.setContentTitle(mStkContext[slotId].mMainCmd.getMenu().title);
   1550             } else {
   1551                 notificationBuilder.setContentTitle("");
   1552             }
   1553             notificationBuilder
   1554                     .setSmallIcon(com.android.internal.R.drawable.stat_notify_sim_toolkit);
   1555             notificationBuilder.setContentIntent(pendingIntent);
   1556             notificationBuilder.setOngoing(true);
   1557             // Set text and icon for the status bar and notification body.
   1558             if (mStkContext[slotId].mIdleModeTextCmd.hasIconLoadFailed() ||
   1559                     !msg.iconSelfExplanatory) {
   1560                 notificationBuilder.setContentText(msg.text);
   1561                 notificationBuilder.setTicker(msg.text);
   1562             }
   1563             if (msg.icon != null) {
   1564                 notificationBuilder.setLargeIcon(msg.icon);
   1565             } else {
   1566                 Bitmap bitmapIcon = BitmapFactory.decodeResource(StkAppService.this
   1567                     .getResources().getSystem(),
   1568                     com.android.internal.R.drawable.stat_notify_sim_toolkit);
   1569                 notificationBuilder.setLargeIcon(bitmapIcon);
   1570             }
   1571             notificationBuilder.setColor(mContext.getResources().getColor(
   1572                     com.android.internal.R.color.system_notification_accent_color));
   1573             mNotificationManager.notify(getNotificationId(slotId), notificationBuilder.build());
   1574         }
   1575     }
   1576 
   1577     private void launchToneDialog(int slotId) {
   1578         Intent newIntent = new Intent(this, ToneDialog.class);
   1579         String uriString = STK_TONE_URI + slotId;
   1580         Uri uriData = Uri.parse(uriString);
   1581         //Set unique URI to create a new instance of activity for different slotId.
   1582         CatLog.d(LOG_TAG, "launchToneDialog, slotId: " + slotId);
   1583         newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1584                 | Intent.FLAG_ACTIVITY_NO_HISTORY
   1585                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
   1586                 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId));
   1587         newIntent.putExtra("TEXT", mStkContext[slotId].mCurrentCmd.geTextMessage());
   1588         newIntent.putExtra("TONE", mStkContext[slotId].mCurrentCmd.getToneSettings());
   1589         newIntent.putExtra(SLOT_ID, slotId);
   1590         newIntent.setData(uriData);
   1591         startActivity(newIntent);
   1592     }
   1593 
   1594     private void launchOpenChannelDialog(final int slotId) {
   1595         TextMessage msg = mStkContext[slotId].mCurrentCmd.geTextMessage();
   1596         if (msg == null) {
   1597             CatLog.d(LOG_TAG, "msg is null, return here");
   1598             return;
   1599         }
   1600 
   1601         msg.title = getResources().getString(R.string.stk_dialog_title);
   1602         if (msg.text == null) {
   1603             msg.text = getResources().getString(R.string.default_open_channel_msg);
   1604         }
   1605 
   1606         final AlertDialog dialog = new AlertDialog.Builder(mContext)
   1607                     .setIconAttribute(android.R.attr.alertDialogIcon)
   1608                     .setTitle(msg.title)
   1609                     .setMessage(msg.text)
   1610                     .setCancelable(false)
   1611                     .setPositiveButton(getResources().getString(R.string.stk_dialog_accept),
   1612                                        new DialogInterface.OnClickListener() {
   1613                         public void onClick(DialogInterface dialog, int which) {
   1614                             Bundle args = new Bundle();
   1615                             args.putInt(RES_ID, RES_ID_CHOICE);
   1616                             args.putInt(CHOICE, YES);
   1617                             Message message = mServiceHandler.obtainMessage();
   1618                             message.arg1 = OP_RESPONSE;
   1619                             message.arg2 = slotId;
   1620                             message.obj = args;
   1621                             mServiceHandler.sendMessage(message);
   1622                         }
   1623                     })
   1624                     .setNegativeButton(getResources().getString(R.string.stk_dialog_reject),
   1625                                        new DialogInterface.OnClickListener() {
   1626                         public void onClick(DialogInterface dialog, int which) {
   1627                             Bundle args = new Bundle();
   1628                             args.putInt(RES_ID, RES_ID_CHOICE);
   1629                             args.putInt(CHOICE, NO);
   1630                             Message message = mServiceHandler.obtainMessage();
   1631                             message.arg1 = OP_RESPONSE;
   1632                             message.arg2 = slotId;
   1633                             message.obj = args;
   1634                             mServiceHandler.sendMessage(message);
   1635                         }
   1636                     })
   1637                     .create();
   1638 
   1639         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1640         if (!mContext.getResources().getBoolean(
   1641                 com.android.internal.R.bool.config_sf_slowBlur)) {
   1642             dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
   1643         }
   1644 
   1645         dialog.show();
   1646     }
   1647 
   1648     private void launchTransientEventMessage(int slotId) {
   1649         TextMessage msg = mStkContext[slotId].mCurrentCmd.geTextMessage();
   1650         if (msg == null) {
   1651             CatLog.d(LOG_TAG, "msg is null, return here");
   1652             return;
   1653         }
   1654 
   1655         msg.title = getResources().getString(R.string.stk_dialog_title);
   1656 
   1657         final AlertDialog dialog = new AlertDialog.Builder(mContext)
   1658                     .setIconAttribute(android.R.attr.alertDialogIcon)
   1659                     .setTitle(msg.title)
   1660                     .setMessage(msg.text)
   1661                     .setCancelable(false)
   1662                     .setPositiveButton(getResources().getString(android.R.string.ok),
   1663                                        new DialogInterface.OnClickListener() {
   1664                         public void onClick(DialogInterface dialog, int which) {
   1665                         }
   1666                     })
   1667                     .create();
   1668 
   1669         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1670         if (!mContext.getResources().getBoolean(
   1671                 com.android.internal.R.bool.config_sf_slowBlur)) {
   1672             dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
   1673         }
   1674 
   1675         dialog.show();
   1676     }
   1677 
   1678     private int getNotificationId(int slotId) {
   1679         int notifyId = STK_NOTIFICATION_ID;
   1680         if (slotId >= 0 && slotId < mSimCount) {
   1681             notifyId += slotId;
   1682         } else {
   1683             CatLog.d(LOG_TAG, "invalid slotId: " + slotId);
   1684         }
   1685         CatLog.d(LOG_TAG, "getNotificationId, slotId: " + slotId + ", notifyId: " + notifyId);
   1686         return notifyId;
   1687     }
   1688 
   1689     private String getItemName(int itemId, int slotId) {
   1690         Menu menu = mStkContext[slotId].mCurrentCmd.getMenu();
   1691         if (menu == null) {
   1692             return null;
   1693         }
   1694         for (Item item : menu.items) {
   1695             if (item.id == itemId) {
   1696                 return item.text;
   1697             }
   1698         }
   1699         return null;
   1700     }
   1701 
   1702     private boolean removeMenu(int slotId) {
   1703         try {
   1704             if (mStkContext[slotId].mCurrentMenu.items.size() == 1 &&
   1705                 mStkContext[slotId].mCurrentMenu.items.get(0) == null) {
   1706                 mStkContext[slotId].mSetupMenuState = STATE_NOT_EXIST;
   1707                 return true;
   1708             }
   1709         } catch (NullPointerException e) {
   1710             CatLog.d(LOG_TAG, "Unable to get Menu's items size");
   1711             mStkContext[slotId].mSetupMenuState = STATE_NOT_EXIST;
   1712             return true;
   1713         }
   1714         mStkContext[slotId].mSetupMenuState = STATE_EXIST;
   1715         return false;
   1716     }
   1717 
   1718     StkContext getStkContext(int slotId) {
   1719         if (slotId >= 0 && slotId < mSimCount) {
   1720             return mStkContext[slotId];
   1721         } else {
   1722             CatLog.d(LOG_TAG, "invalid slotId: " + slotId);
   1723             return null;
   1724         }
   1725     }
   1726 
   1727     private void handleAlphaNotify(Bundle args) {
   1728         String alphaString = args.getString(AppInterface.ALPHA_STRING);
   1729 
   1730         CatLog.d(this, "Alpha string received from card: " + alphaString);
   1731         Toast toast = Toast.makeText(sInstance, alphaString, Toast.LENGTH_LONG);
   1732         toast.setGravity(Gravity.TOP, 0, 0);
   1733         toast.show();
   1734     }
   1735 }
   1736