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