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.KeyguardManager;
     23 import android.app.Notification;
     24 import android.app.NotificationChannel;
     25 import android.app.NotificationManager;
     26 import android.app.PendingIntent;
     27 import android.app.Service;
     28 import android.app.Activity;
     29 import android.app.ActivityManagerNative;
     30 import android.app.IProcessObserver;
     31 import android.content.BroadcastReceiver;
     32 import android.content.Context;
     33 import android.content.DialogInterface;
     34 import android.content.Intent;
     35 import android.content.pm.PackageManager;
     36 import android.content.pm.ResolveInfo;
     37 import android.content.res.Configuration;
     38 import android.content.res.Resources;
     39 import android.content.res.Resources.NotFoundException;
     40 import android.graphics.Bitmap;
     41 import android.graphics.BitmapFactory;
     42 import android.media.RingtoneManager;
     43 import android.net.Uri;
     44 import android.os.Bundle;
     45 import android.os.Handler;
     46 import android.os.IBinder;
     47 import android.os.Looper;
     48 import android.os.Message;
     49 import android.os.Parcel;
     50 import android.os.PersistableBundle;
     51 import android.os.PowerManager;
     52 import android.os.RemoteException;
     53 import android.os.ServiceManager;
     54 import android.os.SystemProperties;
     55 import android.os.Vibrator;
     56 import android.provider.Settings;
     57 import android.support.v4.content.LocalBroadcastManager;
     58 import android.telephony.CarrierConfigManager;
     59 import android.telephony.SubscriptionManager;
     60 import android.telephony.TelephonyManager;
     61 import android.text.TextUtils;
     62 import android.view.Gravity;
     63 import android.view.IWindowManager;
     64 import android.view.LayoutInflater;
     65 import android.view.View;
     66 import android.view.WindowManager;
     67 import android.view.WindowManagerPolicyConstants;
     68 import android.widget.ImageView;
     69 import android.widget.TextView;
     70 import android.widget.Toast;
     71 import android.content.IntentFilter;
     72 
     73 import com.android.internal.telephony.cat.AppInterface;
     74 import com.android.internal.telephony.cat.Input;
     75 import com.android.internal.telephony.cat.LaunchBrowserMode;
     76 import com.android.internal.telephony.cat.Menu;
     77 import com.android.internal.telephony.cat.Item;
     78 import com.android.internal.telephony.cat.ResultCode;
     79 import com.android.internal.telephony.cat.CatCmdMessage;
     80 import com.android.internal.telephony.cat.CatCmdMessage.BrowserSettings;
     81 import com.android.internal.telephony.cat.CatCmdMessage.SetupEventListSettings;
     82 import com.android.internal.telephony.cat.CatLog;
     83 import com.android.internal.telephony.cat.CatResponseMessage;
     84 import com.android.internal.telephony.cat.TextMessage;
     85 import com.android.internal.telephony.cat.ToneSettings;
     86 import com.android.internal.telephony.uicc.IccRefreshResponse;
     87 import com.android.internal.telephony.PhoneConstants;
     88 import com.android.internal.telephony.GsmAlphabet;
     89 import com.android.internal.telephony.cat.CatService;
     90 
     91 import java.util.Iterator;
     92 import java.util.LinkedList;
     93 import java.lang.System;
     94 import java.util.List;
     95 
     96 import static com.android.internal.telephony.cat.CatCmdMessage.
     97                    SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT;
     98 import static com.android.internal.telephony.cat.CatCmdMessage.
     99                    SetupEventListConstants.LANGUAGE_SELECTION_EVENT;
    100 import static com.android.internal.telephony.cat.CatCmdMessage.
    101                    SetupEventListConstants.USER_ACTIVITY_EVENT;
    102 
    103 /**
    104  * SIM toolkit application level service. Interacts with Telephopny messages,
    105  * application's launch and user input from STK UI elements.
    106  *
    107  */
    108 public class StkAppService extends Service implements Runnable {
    109 
    110     // members
    111     protected class StkContext {
    112         protected CatCmdMessage mMainCmd = null;
    113         protected CatCmdMessage mCurrentCmd = null;
    114         protected CatCmdMessage mCurrentMenuCmd = null;
    115         protected Menu mCurrentMenu = null;
    116         protected String lastSelectedItem = null;
    117         protected boolean mMenuIsVisible = false;
    118         protected boolean mIsInputPending = false;
    119         protected boolean mIsMenuPending = false;
    120         protected boolean mIsDialogPending = false;
    121         protected boolean mNotificationOnKeyguard = false;
    122         protected boolean mNoResponseFromUser = false;
    123         protected boolean launchBrowser = false;
    124         protected BrowserSettings mBrowserSettings = null;
    125         protected LinkedList<DelayedCmd> mCmdsQ = null;
    126         protected boolean mCmdInProgress = false;
    127         protected int mStkServiceState = STATE_UNKNOWN;
    128         protected int mSetupMenuState = STATE_UNKNOWN;
    129         protected int mMenuState = StkMenuActivity.STATE_INIT;
    130         protected int mOpCode = -1;
    131         private Activity mActivityInstance = null;
    132         private Activity mDialogInstance = null;
    133         private Activity mImmediateDialogInstance = null;
    134         private int mSlotId = 0;
    135         private SetupEventListSettings mSetupEventListSettings = null;
    136         private boolean mClearSelectItem = false;
    137         private boolean mDisplayTextDlgIsVisibile = false;
    138         private CatCmdMessage mCurrentSetupEventCmd = null;
    139         private CatCmdMessage mIdleModeTextCmd = null;
    140         private boolean mIdleModeTextVisible = false;
    141         final synchronized void setPendingActivityInstance(Activity act) {
    142             CatLog.d(this, "setPendingActivityInstance act : " + mSlotId + ", " + act);
    143             callSetActivityInstMsg(OP_SET_ACT_INST, mSlotId, act);
    144         }
    145         final synchronized Activity getPendingActivityInstance() {
    146             CatLog.d(this, "getPendingActivityInstance act : " + mSlotId + ", " +
    147                     mActivityInstance);
    148             return mActivityInstance;
    149         }
    150         final synchronized void setPendingDialogInstance(Activity act) {
    151             CatLog.d(this, "setPendingDialogInstance act : " + mSlotId + ", " + act);
    152             callSetActivityInstMsg(OP_SET_DAL_INST, mSlotId, act);
    153         }
    154         final synchronized Activity getPendingDialogInstance() {
    155             CatLog.d(this, "getPendingDialogInstance act : " + mSlotId + ", " +
    156                     mDialogInstance);
    157             return mDialogInstance;
    158         }
    159         final synchronized void setImmediateDialogInstance(Activity act) {
    160             CatLog.d(this, "setImmediateDialogInstance act : " + mSlotId + ", " + act);
    161             callSetActivityInstMsg(OP_SET_IMMED_DAL_INST, mSlotId, act);
    162         }
    163         final synchronized Activity getImmediateDialogInstance() {
    164             CatLog.d(this, "getImmediateDialogInstance act : " + mSlotId + ", " +
    165                     mImmediateDialogInstance);
    166             return mImmediateDialogInstance;
    167         }
    168     }
    169 
    170     private volatile Looper mServiceLooper;
    171     private volatile ServiceHandler mServiceHandler;
    172     private Context mContext = null;
    173     private NotificationManager mNotificationManager = null;
    174     static StkAppService sInstance = null;
    175     private AppInterface[] mStkService = null;
    176     private StkContext[] mStkContext = null;
    177     private int mSimCount = 0;
    178     private IProcessObserver.Stub mProcessObserver = null;
    179     private TonePlayer mTonePlayer = null;
    180     private Vibrator mVibrator = null;
    181     private BroadcastReceiver mUserActivityReceiver = null;
    182 
    183     // Used for setting FLAG_ACTIVITY_NO_USER_ACTION when
    184     // creating an intent.
    185     private enum InitiatedByUserAction {
    186         yes,            // The action was started via a user initiated action
    187         unknown,        // Not known for sure if user initated the action
    188     }
    189 
    190     // constants
    191     static final String OPCODE = "op";
    192     static final String CMD_MSG = "cmd message";
    193     static final String RES_ID = "response id";
    194     static final String MENU_SELECTION = "menu selection";
    195     static final String INPUT = "input";
    196     static final String HELP = "help";
    197     static final String CONFIRMATION = "confirm";
    198     static final String CHOICE = "choice";
    199     static final String SLOT_ID = "SLOT_ID";
    200     static final String STK_CMD = "STK CMD";
    201     static final String STK_DIALOG_URI = "stk://com.android.stk/dialog/";
    202     static final String STK_MENU_URI = "stk://com.android.stk/menu/";
    203     static final String STK_INPUT_URI = "stk://com.android.stk/input/";
    204     static final String STK_TONE_URI = "stk://com.android.stk/tone/";
    205     static final String FINISH_TONE_ACTIVITY_ACTION =
    206                                 "android.intent.action.stk.finish_activity";
    207 
    208     // These below constants are used for SETUP_EVENT_LIST
    209     static final String SETUP_EVENT_TYPE = "event";
    210     static final String SETUP_EVENT_CAUSE = "cause";
    211 
    212     // operations ids for different service functionality.
    213     static final int OP_CMD = 1;
    214     static final int OP_RESPONSE = 2;
    215     static final int OP_LAUNCH_APP = 3;
    216     static final int OP_END_SESSION = 4;
    217     static final int OP_BOOT_COMPLETED = 5;
    218     private static final int OP_DELAYED_MSG = 6;
    219     static final int OP_CARD_STATUS_CHANGED = 7;
    220     static final int OP_SET_ACT_INST = 8;
    221     static final int OP_SET_DAL_INST = 9;
    222     static final int OP_LOCALE_CHANGED = 10;
    223     static final int OP_ALPHA_NOTIFY = 11;
    224     static final int OP_IDLE_SCREEN = 12;
    225     static final int OP_SET_IMMED_DAL_INST = 13;
    226 
    227     //Invalid SetupEvent
    228     static final int INVALID_SETUP_EVENT = 0xFF;
    229 
    230     // Message id to signal stop tone due to play tone timeout.
    231     private static final int OP_STOP_TONE = 16;
    232 
    233     // Message id to signal stop tone on user keyback.
    234     static final int OP_STOP_TONE_USER = 17;
    235 
    236     // Message id to remove stop tone message from queue.
    237     private static final int STOP_TONE_WHAT = 100;
    238 
    239     // Message id to send user activity event to card.
    240     private static final int OP_USER_ACTIVITY = 20;
    241 
    242     // Response ids
    243     static final int RES_ID_MENU_SELECTION = 11;
    244     static final int RES_ID_INPUT = 12;
    245     static final int RES_ID_CONFIRM = 13;
    246     static final int RES_ID_DONE = 14;
    247     static final int RES_ID_CHOICE = 15;
    248 
    249     static final int RES_ID_TIMEOUT = 20;
    250     static final int RES_ID_BACKWARD = 21;
    251     static final int RES_ID_END_SESSION = 22;
    252     static final int RES_ID_EXIT = 23;
    253     static final int RES_ID_ERROR = 24;
    254 
    255     static final int YES = 1;
    256     static final int NO = 0;
    257 
    258     static final int STATE_UNKNOWN = -1;
    259     static final int STATE_NOT_EXIST = 0;
    260     static final int STATE_EXIST = 1;
    261 
    262     private static final String PACKAGE_NAME = "com.android.stk";
    263     private static final String STK_MENU_ACTIVITY_NAME = PACKAGE_NAME + ".StkMenuActivity";
    264     private static final String STK_INPUT_ACTIVITY_NAME = PACKAGE_NAME + ".StkInputActivity";
    265     private static final String STK_DIALOG_ACTIVITY_NAME = PACKAGE_NAME + ".StkDialogActivity";
    266     // Notification id used to display Idle Mode text in NotificationManager.
    267     private static final int STK_NOTIFICATION_ID = 333;
    268     // Notification channel containing all mobile service messages notifications.
    269     private static final String STK_NOTIFICATION_CHANNEL_ID = "mobileServiceMessages";
    270 
    271     private static final String LOG_TAG = new Object(){}.getClass().getEnclosingClass().getName();
    272 
    273     static final String SESSION_ENDED = "session_ended";
    274 
    275     // Inner class used for queuing telephony messages (proactive commands,
    276     // session end) while the service is busy processing a previous message.
    277     private class DelayedCmd {
    278         // members
    279         int id;
    280         CatCmdMessage msg;
    281         int slotId;
    282 
    283         DelayedCmd(int id, CatCmdMessage msg, int slotId) {
    284             this.id = id;
    285             this.msg = msg;
    286             this.slotId = slotId;
    287         }
    288     }
    289 
    290     // system property to set the STK specific default url for launch browser proactive cmds
    291     private static final String STK_BROWSER_DEFAULT_URL_SYSPROP = "persist.radio.stk.default_url";
    292 
    293     private static final int NOTIFICATION_ON_KEYGUARD = 1;
    294     private static final long[] VIBRATION_PATTERN = new long[] { 0, 350, 250, 350 };
    295     private BroadcastReceiver mUserPresentReceiver = null;
    296 
    297     @Override
    298     public void onCreate() {
    299         CatLog.d(LOG_TAG, "onCreate()+");
    300         // Initialize members
    301         int i = 0;
    302         mContext = getBaseContext();
    303         mSimCount = TelephonyManager.from(mContext).getSimCount();
    304         CatLog.d(LOG_TAG, "simCount: " + mSimCount);
    305         mStkService = new AppInterface[mSimCount];
    306         mStkContext = new StkContext[mSimCount];
    307 
    308         for (i = 0; i < mSimCount; i++) {
    309             CatLog.d(LOG_TAG, "slotId: " + i);
    310             mStkService[i] = CatService.getInstance(i);
    311             mStkContext[i] = new StkContext();
    312             mStkContext[i].mSlotId = i;
    313             mStkContext[i].mCmdsQ = new LinkedList<DelayedCmd>();
    314         }
    315 
    316         Thread serviceThread = new Thread(null, this, "Stk App Service");
    317         serviceThread.start();
    318         mNotificationManager = (NotificationManager) mContext
    319                 .getSystemService(Context.NOTIFICATION_SERVICE);
    320         sInstance = this;
    321     }
    322 
    323     @Override
    324     public void onStart(Intent intent, int startId) {
    325         if (intent == null) {
    326             CatLog.d(LOG_TAG, "StkAppService onStart intent is null so return");
    327             return;
    328         }
    329 
    330         Bundle args = intent.getExtras();
    331         if (args == null) {
    332             CatLog.d(LOG_TAG, "StkAppService onStart args is null so return");
    333             return;
    334         }
    335 
    336         int op = args.getInt(OPCODE);
    337         int slotId = 0;
    338         int i = 0;
    339         if (op != OP_BOOT_COMPLETED) {
    340             slotId = args.getInt(SLOT_ID);
    341         }
    342         CatLog.d(LOG_TAG, "onStart sim id: " + slotId + ", op: " + op + ", *****");
    343         if ((slotId >= 0 && slotId < mSimCount) && mStkService[slotId] == null) {
    344             mStkService[slotId] = CatService.getInstance(slotId);
    345             if (mStkService[slotId] == null) {
    346                 CatLog.d(LOG_TAG, "mStkService is: " + mStkContext[slotId].mStkServiceState);
    347                 mStkContext[slotId].mStkServiceState = STATE_NOT_EXIST;
    348                 //Check other StkService state.
    349                 //If all StkServices are not available, stop itself and uninstall apk.
    350                 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) {
    351                     if (i != slotId
    352                             && (mStkService[i] != null)
    353                             && (mStkContext[i].mStkServiceState == STATE_UNKNOWN
    354                             || mStkContext[i].mStkServiceState == STATE_EXIST)) {
    355                        break;
    356                    }
    357                 }
    358             } else {
    359                 mStkContext[slotId].mStkServiceState = STATE_EXIST;
    360             }
    361             if (i == mSimCount) {
    362                 stopSelf();
    363                 StkAppInstaller.unInstall(mContext);
    364                 return;
    365             }
    366         }
    367 
    368         waitForLooper();
    369 
    370         Message msg = mServiceHandler.obtainMessage();
    371         msg.arg1 = op;
    372         msg.arg2 = slotId;
    373         switch(msg.arg1) {
    374         case OP_CMD:
    375             msg.obj = args.getParcelable(CMD_MSG);
    376             break;
    377         case OP_RESPONSE:
    378         case OP_CARD_STATUS_CHANGED:
    379         case OP_LOCALE_CHANGED:
    380         case OP_ALPHA_NOTIFY:
    381         case OP_IDLE_SCREEN:
    382             msg.obj = args;
    383             /* falls through */
    384         case OP_LAUNCH_APP:
    385         case OP_END_SESSION:
    386         case OP_BOOT_COMPLETED:
    387             break;
    388         case OP_STOP_TONE_USER:
    389             msg.obj = args;
    390             msg.what = STOP_TONE_WHAT;
    391             break;
    392         default:
    393             return;
    394         }
    395         mServiceHandler.sendMessage(msg);
    396     }
    397 
    398     @Override
    399     public void onDestroy() {
    400         CatLog.d(LOG_TAG, "onDestroy()");
    401         unregisterUserActivityReceiver();
    402         unregisterProcessObserver();
    403         sInstance = null;
    404         waitForLooper();
    405         mServiceLooper.quit();
    406     }
    407 
    408     @Override
    409     public IBinder onBind(Intent intent) {
    410         return null;
    411     }
    412 
    413     public void run() {
    414         Looper.prepare();
    415 
    416         mServiceLooper = Looper.myLooper();
    417         mServiceHandler = new ServiceHandler();
    418 
    419         Looper.loop();
    420     }
    421 
    422     /*
    423      * Package api used by StkMenuActivity to indicate if its on the foreground.
    424      */
    425     void indicateMenuVisibility(boolean visibility, int slotId) {
    426         if (slotId >= 0 && slotId < mSimCount) {
    427             mStkContext[slotId].mMenuIsVisible = visibility;
    428         }
    429     }
    430 
    431     /*
    432      * Package api used by StkDialogActivity to indicate if its on the foreground.
    433      */
    434     void setDisplayTextDlgVisibility(boolean visibility, int slotId) {
    435         if (slotId >= 0 && slotId < mSimCount) {
    436             mStkContext[slotId].mDisplayTextDlgIsVisibile = visibility;
    437         }
    438     }
    439 
    440     boolean isInputPending(int slotId) {
    441         if (slotId >= 0 && slotId < mSimCount) {
    442             CatLog.d(LOG_TAG, "isInputFinishBySrv: " + mStkContext[slotId].mIsInputPending);
    443             return mStkContext[slotId].mIsInputPending;
    444         }
    445         return false;
    446     }
    447 
    448     boolean isMenuPending(int slotId) {
    449         if (slotId >= 0 && slotId < mSimCount) {
    450             CatLog.d(LOG_TAG, "isMenuPending: " + mStkContext[slotId].mIsMenuPending);
    451             return mStkContext[slotId].mIsMenuPending;
    452         }
    453         return false;
    454     }
    455 
    456     boolean isDialogPending(int slotId) {
    457         if (slotId >= 0 && slotId < mSimCount) {
    458             CatLog.d(LOG_TAG, "isDialogPending: " + mStkContext[slotId].mIsDialogPending);
    459             return mStkContext[slotId].mIsDialogPending;
    460         }
    461         return false;
    462     }
    463 
    464     boolean isMainMenuAvailable(int slotId) {
    465         if (slotId >= 0 && slotId < mSimCount) {
    466             // The main menu can handle the next user operation if the previous session finished.
    467             return (mStkContext[slotId].lastSelectedItem == null) ? true : false;
    468         }
    469         return false;
    470     }
    471 
    472     /*
    473      * Package api used by StkMenuActivity to get its Menu parameter.
    474      */
    475     Menu getMenu(int slotId) {
    476         CatLog.d(LOG_TAG, "StkAppService, getMenu, sim id: " + slotId);
    477         if (slotId >=0 && slotId < mSimCount) {
    478             return mStkContext[slotId].mCurrentMenu;
    479         } else {
    480             return null;
    481         }
    482     }
    483 
    484     /*
    485      * Package api used by StkMenuActivity to get its Main Menu parameter.
    486      */
    487     Menu getMainMenu(int slotId) {
    488         CatLog.d(LOG_TAG, "StkAppService, getMainMenu, sim id: " + slotId);
    489         if (slotId >=0 && slotId < mSimCount && (mStkContext[slotId].mMainCmd != null)) {
    490             Menu menu = mStkContext[slotId].mMainCmd.getMenu();
    491             if (menu != null && mSimCount > PhoneConstants.MAX_PHONE_COUNT_SINGLE_SIM) {
    492                 // If alpha identifier or icon identifier with the self-explanatory qualifier is
    493                 // specified in SET-UP MENU command, it should be more prioritized than preset ones.
    494                 if (menu.title == null
    495                         && (menu.titleIcon == null || !menu.titleIconSelfExplanatory)) {
    496                     StkMenuConfig config = StkMenuConfig.getInstance(getApplicationContext());
    497                     String label = config.getLabel(slotId);
    498                     Bitmap icon = config.getIcon(slotId);
    499                     if (label != null || icon != null) {
    500                         Parcel parcel = Parcel.obtain();
    501                         menu.writeToParcel(parcel, 0);
    502                         parcel.setDataPosition(0);
    503                         menu = Menu.CREATOR.createFromParcel(parcel);
    504                         parcel.recycle();
    505                         menu.title = label;
    506                         menu.titleIcon = icon;
    507                         menu.titleIconSelfExplanatory = false;
    508                     }
    509                 }
    510             }
    511             return menu;
    512         } else {
    513             return null;
    514         }
    515     }
    516 
    517     /*
    518      * Package api used by UI Activities and Dialogs to communicate directly
    519      * with the service to deliver state information and parameters.
    520      */
    521     static StkAppService getInstance() {
    522         return sInstance;
    523     }
    524 
    525     private void waitForLooper() {
    526         while (mServiceHandler == null) {
    527             synchronized (this) {
    528                 try {
    529                     wait(100);
    530                 } catch (InterruptedException e) {
    531                 }
    532             }
    533         }
    534     }
    535 
    536     private final class ServiceHandler extends Handler {
    537         @Override
    538         public void handleMessage(Message msg) {
    539             if(null == msg) {
    540                 CatLog.d(LOG_TAG, "ServiceHandler handleMessage msg is null");
    541                 return;
    542             }
    543             int opcode = msg.arg1;
    544             int slotId = msg.arg2;
    545 
    546             CatLog.d(LOG_TAG, "handleMessage opcode[" + opcode + "], sim id[" + slotId + "]");
    547             if (opcode == OP_CMD && msg.obj != null &&
    548                     ((CatCmdMessage)msg.obj).getCmdType()!= null) {
    549                 CatLog.d(LOG_TAG, "cmdName[" + ((CatCmdMessage)msg.obj).getCmdType().name() + "]");
    550             }
    551             mStkContext[slotId].mOpCode = opcode;
    552             switch (opcode) {
    553             case OP_LAUNCH_APP:
    554                 if (mStkContext[slotId].mMainCmd == null) {
    555                     CatLog.d(LOG_TAG, "mMainCmd is null");
    556                     // nothing todo when no SET UP MENU command didn't arrive.
    557                     return;
    558                 }
    559                 CatLog.d(LOG_TAG, "handleMessage OP_LAUNCH_APP - mCmdInProgress[" +
    560                         mStkContext[slotId].mCmdInProgress + "]");
    561 
    562                 //If there is a pending activity for the slot id,
    563                 //just finish it and create a new one to handle the pending command.
    564                 cleanUpInstanceStackBySlot(slotId);
    565 
    566                 CatLog.d(LOG_TAG, "Current cmd type: " +
    567                         mStkContext[slotId].mCurrentCmd.getCmdType());
    568                 //Restore the last command from stack by slot id.
    569                 restoreInstanceFromStackBySlot(slotId);
    570                 break;
    571             case OP_CMD:
    572                 CatLog.d(LOG_TAG, "[OP_CMD]");
    573                 CatCmdMessage cmdMsg = (CatCmdMessage) msg.obj;
    574                 // There are two types of commands:
    575                 // 1. Interactive - user's response is required.
    576                 // 2. Informative - display a message, no interaction with the user.
    577                 //
    578                 // Informative commands can be handled immediately without any delay.
    579                 // Interactive commands can't override each other. So if a command
    580                 // is already in progress, we need to queue the next command until
    581                 // the user has responded or a timeout expired.
    582                 if (!isCmdInteractive(cmdMsg)) {
    583                     handleCmd(cmdMsg, slotId);
    584                 } else {
    585                     if (!mStkContext[slotId].mCmdInProgress) {
    586                         mStkContext[slotId].mCmdInProgress = true;
    587                         handleCmd((CatCmdMessage) msg.obj, slotId);
    588                     } else {
    589                         CatLog.d(LOG_TAG, "[Interactive][in progress]");
    590                         mStkContext[slotId].mCmdsQ.addLast(new DelayedCmd(OP_CMD,
    591                                 (CatCmdMessage) msg.obj, slotId));
    592                     }
    593                 }
    594                 break;
    595             case OP_RESPONSE:
    596                 handleCmdResponse((Bundle) msg.obj, slotId);
    597                 // call delayed commands if needed.
    598                 if (mStkContext[slotId].mCmdsQ.size() != 0) {
    599                     callDelayedMsg(slotId);
    600                 } else {
    601                     mStkContext[slotId].mCmdInProgress = false;
    602                 }
    603                 break;
    604             case OP_END_SESSION:
    605                 if (!mStkContext[slotId].mCmdInProgress) {
    606                     mStkContext[slotId].mCmdInProgress = true;
    607                     handleSessionEnd(slotId);
    608                 } else {
    609                     mStkContext[slotId].mCmdsQ.addLast(
    610                             new DelayedCmd(OP_END_SESSION, null, slotId));
    611                 }
    612                 break;
    613             case OP_BOOT_COMPLETED:
    614                 CatLog.d(LOG_TAG, " OP_BOOT_COMPLETED");
    615                 int i = 0;
    616                 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) {
    617                     if (mStkContext[i].mMainCmd != null) {
    618                         break;
    619                     }
    620                 }
    621                 if (i == mSimCount) {
    622                     StkAppInstaller.unInstall(mContext);
    623                 }
    624                 break;
    625             case OP_DELAYED_MSG:
    626                 handleDelayedCmd(slotId);
    627                 break;
    628             case OP_CARD_STATUS_CHANGED:
    629                 CatLog.d(LOG_TAG, "Card/Icc Status change received");
    630                 handleCardStatusChangeAndIccRefresh((Bundle) msg.obj, slotId);
    631                 break;
    632             case OP_SET_ACT_INST:
    633                 Activity act = (Activity) msg.obj;
    634                 if (mStkContext[slotId].mActivityInstance != act) {
    635                     CatLog.d(LOG_TAG, "Set activity instance - " + act);
    636                     Activity previous = mStkContext[slotId].mActivityInstance;
    637                     mStkContext[slotId].mActivityInstance = act;
    638                     // Finish the previous one if it has not been finished yet somehow.
    639                     if (previous != null && !previous.isDestroyed() && !previous.isFinishing()) {
    640                         CatLog.d(LOG_TAG, "Finish the previous pending activity - " + previous);
    641                         previous.finish();
    642                     }
    643                 }
    644                 break;
    645             case OP_SET_DAL_INST:
    646                 Activity dal = (Activity) msg.obj;
    647                 CatLog.d(LOG_TAG, "Set dialog instance. " + dal);
    648                 mStkContext[slotId].mDialogInstance = dal;
    649                 break;
    650             case OP_SET_IMMED_DAL_INST:
    651                 Activity immedDal = (Activity) msg.obj;
    652                 CatLog.d(LOG_TAG, "Set dialog instance for immediate response. " + immedDal);
    653                 mStkContext[slotId].mImmediateDialogInstance = immedDal;
    654                 break;
    655             case OP_LOCALE_CHANGED:
    656                 CatLog.d(this, "Locale Changed");
    657                 for (int slot = PhoneConstants.SIM_ID_1; slot < mSimCount; slot++) {
    658                     checkForSetupEvent(LANGUAGE_SELECTION_EVENT, (Bundle) msg.obj, slot);
    659                 }
    660                 // rename all registered notification channels on locale change
    661                 createAllChannels();
    662                 break;
    663             case OP_ALPHA_NOTIFY:
    664                 handleAlphaNotify((Bundle) msg.obj);
    665                 break;
    666             case OP_IDLE_SCREEN:
    667                for (int slot = 0; slot < mSimCount; slot++) {
    668                     if (mStkContext[slot] != null) {
    669                         handleIdleScreen(slot);
    670                     }
    671                 }
    672                 break;
    673             case OP_STOP_TONE_USER:
    674             case OP_STOP_TONE:
    675                 CatLog.d(this, "Stop tone");
    676                 handleStopTone(msg, slotId);
    677                 break;
    678             case OP_USER_ACTIVITY:
    679                 for (int slot = PhoneConstants.SIM_ID_1; slot < mSimCount; slot++) {
    680                     checkForSetupEvent(USER_ACTIVITY_EVENT, null, slot);
    681                 }
    682                 break;
    683             }
    684         }
    685 
    686         private void handleCardStatusChangeAndIccRefresh(Bundle args, int slotId) {
    687             boolean cardStatus = args.getBoolean(AppInterface.CARD_STATUS);
    688 
    689             CatLog.d(LOG_TAG, "CardStatus: " + cardStatus);
    690             if (cardStatus == false) {
    691                 CatLog.d(LOG_TAG, "CARD is ABSENT");
    692                 // Uninstall STKAPP, Clear Idle text, Stop StkAppService
    693                 cancelIdleText(slotId);
    694                 mStkContext[slotId].mCurrentMenu = null;
    695                 mStkContext[slotId].mMainCmd = null;
    696                 if (isAllOtherCardsAbsent(slotId)) {
    697                     CatLog.d(LOG_TAG, "All CARDs are ABSENT");
    698                     StkAppInstaller.unInstall(mContext);
    699                     stopSelf();
    700                 }
    701             } else {
    702                 IccRefreshResponse state = new IccRefreshResponse();
    703                 state.refreshResult = args.getInt(AppInterface.REFRESH_RESULT);
    704 
    705                 CatLog.d(LOG_TAG, "Icc Refresh Result: "+ state.refreshResult);
    706                 if ((state.refreshResult == IccRefreshResponse.REFRESH_RESULT_INIT) ||
    707                     (state.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET)) {
    708                     // Clear Idle Text
    709                     cancelIdleText(slotId);
    710                 }
    711 
    712                 if (state.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET) {
    713                     // Uninstall STkmenu
    714                     if (isAllOtherCardsAbsent(slotId)) {
    715                         StkAppInstaller.unInstall(mContext);
    716                     }
    717                     mStkContext[slotId].mCurrentMenu = null;
    718                     mStkContext[slotId].mMainCmd = null;
    719                 }
    720             }
    721         }
    722     }
    723     /*
    724      * Check if all SIMs are absent except the id of slot equals "slotId".
    725      */
    726     private boolean isAllOtherCardsAbsent(int slotId) {
    727         TelephonyManager mTm = (TelephonyManager) mContext.getSystemService(
    728                 Context.TELEPHONY_SERVICE);
    729         int i = 0;
    730 
    731         for (i = 0; i < mSimCount; i++) {
    732             if (i != slotId && mTm.hasIccCard(i)) {
    733                 break;
    734             }
    735         }
    736         if (i == mSimCount) {
    737             return true;
    738         } else {
    739             return false;
    740         }
    741     }
    742 
    743     /* package */ boolean isScreenIdle() {
    744         ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    745         List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    746         if (tasks == null || tasks.isEmpty()) {
    747             return false;
    748         }
    749 
    750         String top = tasks.get(0).topActivity.getPackageName();
    751         if (top == null) {
    752             return false;
    753         }
    754 
    755         // We can assume that the screen is idle if the home application is in the foreground.
    756         final Intent intent = new Intent(Intent.ACTION_MAIN, null);
    757         intent.addCategory(Intent.CATEGORY_HOME);
    758 
    759         ResolveInfo info = getPackageManager().resolveActivity(intent,
    760                 PackageManager.MATCH_DEFAULT_ONLY);
    761         if (info != null) {
    762             if (top.equals(info.activityInfo.packageName)) {
    763                 return true;
    764             }
    765         }
    766 
    767         return false;
    768     }
    769 
    770     private void handleIdleScreen(int slotId) {
    771 
    772         // If the idle screen event is present in the list need to send the
    773         // response to SIM.
    774         CatLog.d(this, "Need to send IDLE SCREEN Available event to SIM");
    775         checkForSetupEvent(IDLE_SCREEN_AVAILABLE_EVENT, null, slotId);
    776 
    777         if (mStkContext[slotId].mIdleModeTextCmd != null
    778                 && !mStkContext[slotId].mIdleModeTextVisible) {
    779             launchIdleText(slotId);
    780         }
    781     }
    782 
    783     private void sendScreenBusyResponse(int slotId) {
    784         if (mStkContext[slotId].mCurrentCmd == null) {
    785             return;
    786         }
    787         CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentCmd);
    788         CatLog.d(this, "SCREEN_BUSY");
    789         resMsg.setResultCode(ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS);
    790         mStkService[slotId].onCmdResponse(resMsg);
    791         if (mStkContext[slotId].mCmdsQ.size() != 0) {
    792             callDelayedMsg(slotId);
    793         } else {
    794             mStkContext[slotId].mCmdInProgress = false;
    795         }
    796     }
    797 
    798     private void sendResponse(int resId, int slotId, boolean confirm) {
    799         Message msg = mServiceHandler.obtainMessage();
    800         msg.arg1 = OP_RESPONSE;
    801         msg.arg2 = slotId;
    802         Bundle args = new Bundle();
    803         args.putInt(StkAppService.RES_ID, resId);
    804         args.putBoolean(StkAppService.CONFIRMATION, confirm);
    805         msg.obj = args;
    806         mServiceHandler.sendMessage(msg);
    807     }
    808 
    809     private boolean isCmdInteractive(CatCmdMessage cmd) {
    810         switch (cmd.getCmdType()) {
    811         case SEND_DTMF:
    812         case SEND_SMS:
    813         case SEND_SS:
    814         case SEND_USSD:
    815         case SET_UP_IDLE_MODE_TEXT:
    816         case SET_UP_MENU:
    817         case CLOSE_CHANNEL:
    818         case RECEIVE_DATA:
    819         case SEND_DATA:
    820         case SET_UP_EVENT_LIST:
    821             return false;
    822         }
    823 
    824         return true;
    825     }
    826 
    827     private void handleDelayedCmd(int slotId) {
    828         CatLog.d(LOG_TAG, "handleDelayedCmd, slotId: " + slotId);
    829         if (mStkContext[slotId].mCmdsQ.size() != 0) {
    830             DelayedCmd cmd = mStkContext[slotId].mCmdsQ.poll();
    831             if (cmd != null) {
    832                 CatLog.d(LOG_TAG, "handleDelayedCmd - queue size: " +
    833                         mStkContext[slotId].mCmdsQ.size() +
    834                         " id: " + cmd.id + "sim id: " + cmd.slotId);
    835                 switch (cmd.id) {
    836                 case OP_CMD:
    837                     handleCmd(cmd.msg, cmd.slotId);
    838                     break;
    839                 case OP_END_SESSION:
    840                     handleSessionEnd(cmd.slotId);
    841                     break;
    842                 }
    843             }
    844         }
    845     }
    846 
    847     private void callDelayedMsg(int slotId) {
    848         Message msg = mServiceHandler.obtainMessage();
    849         msg.arg1 = OP_DELAYED_MSG;
    850         msg.arg2 = slotId;
    851         mServiceHandler.sendMessage(msg);
    852     }
    853 
    854     private void callSetActivityInstMsg(int inst_type, int slotId, Object obj) {
    855         Message msg = mServiceHandler.obtainMessage();
    856         msg.obj = obj;
    857         msg.arg1 = inst_type;
    858         msg.arg2 = slotId;
    859         mServiceHandler.sendMessage(msg);
    860     }
    861 
    862     private void handleSessionEnd(int slotId) {
    863         // We should finish all pending activity if receiving END SESSION command.
    864         cleanUpInstanceStackBySlot(slotId);
    865 
    866         mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd;
    867         CatLog.d(LOG_TAG, "[handleSessionEnd] - mCurrentCmd changed to mMainCmd!.");
    868         mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mMainCmd;
    869         CatLog.d(LOG_TAG, "slotId: " + slotId + ", mMenuState: " +
    870                 mStkContext[slotId].mMenuState);
    871 
    872         mStkContext[slotId].mIsInputPending = false;
    873         mStkContext[slotId].mIsMenuPending = false;
    874         mStkContext[slotId].mIsDialogPending = false;
    875         mStkContext[slotId].mNoResponseFromUser = false;
    876 
    877         if (mStkContext[slotId].mMainCmd == null) {
    878             CatLog.d(LOG_TAG, "[handleSessionEnd][mMainCmd is null!]");
    879         }
    880         mStkContext[slotId].lastSelectedItem = null;
    881         // In case of SET UP MENU command which removed the app, don't
    882         // update the current menu member.
    883         if (mStkContext[slotId].mCurrentMenu != null && mStkContext[slotId].mMainCmd != null) {
    884             mStkContext[slotId].mCurrentMenu = mStkContext[slotId].mMainCmd.getMenu();
    885         }
    886         CatLog.d(LOG_TAG, "[handleSessionEnd][mMenuState]" + mStkContext[slotId].mMenuIsVisible);
    887 
    888         if (StkMenuActivity.STATE_SECONDARY == mStkContext[slotId].mMenuState) {
    889             mStkContext[slotId].mMenuState = StkMenuActivity.STATE_MAIN;
    890         }
    891 
    892         // Send a local broadcast as a notice that this service handled the session end event.
    893         Intent intent = new Intent(SESSION_ENDED);
    894         intent.putExtra(SLOT_ID, slotId);
    895         LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    896 
    897         if (mStkContext[slotId].mCmdsQ.size() != 0) {
    898             callDelayedMsg(slotId);
    899         } else {
    900             mStkContext[slotId].mCmdInProgress = false;
    901         }
    902         // In case a launch browser command was just confirmed, launch that url.
    903         if (mStkContext[slotId].launchBrowser) {
    904             mStkContext[slotId].launchBrowser = false;
    905             launchBrowser(mStkContext[slotId].mBrowserSettings);
    906         }
    907     }
    908 
    909     // returns true if any Stk related activity already has focus on the screen
    910     boolean isTopOfStack() {
    911         ActivityManager mActivityManager = (ActivityManager) mContext
    912                 .getSystemService(ACTIVITY_SERVICE);
    913         String currentPackageName = null;
    914         List<RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
    915         if (tasks == null || tasks.get(0).topActivity == null) {
    916             return false;
    917         }
    918         currentPackageName = tasks.get(0).topActivity.getPackageName();
    919         if (null != currentPackageName) {
    920             return currentPackageName.equals(PACKAGE_NAME);
    921         }
    922         return false;
    923     }
    924 
    925     /**
    926      * Get the boolean config from carrier config manager.
    927      *
    928      * @param context the context to get carrier service
    929      * @param key config key defined in CarrierConfigManager
    930      * @return boolean value of corresponding key.
    931      */
    932     private static boolean getBooleanCarrierConfig(Context context, String key) {
    933         CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
    934                 Context.CARRIER_CONFIG_SERVICE);
    935         PersistableBundle b = null;
    936         if (configManager != null) {
    937             b = configManager.getConfig();
    938         }
    939         if (b != null) {
    940             return b.getBoolean(key);
    941         } else {
    942             // Return static default defined in CarrierConfigManager.
    943             return CarrierConfigManager.getDefaultConfig().getBoolean(key);
    944         }
    945     }
    946 
    947     private void handleCmd(CatCmdMessage cmdMsg, int slotId) {
    948 
    949         if (cmdMsg == null) {
    950             return;
    951         }
    952         // save local reference for state tracking.
    953         mStkContext[slotId].mCurrentCmd = cmdMsg;
    954         boolean waitForUsersResponse = true;
    955 
    956         mStkContext[slotId].mIsInputPending = false;
    957         mStkContext[slotId].mIsMenuPending = false;
    958         mStkContext[slotId].mIsDialogPending = false;
    959 
    960         CatLog.d(LOG_TAG,"[handleCmd]" + cmdMsg.getCmdType().name());
    961         switch (cmdMsg.getCmdType()) {
    962         case DISPLAY_TEXT:
    963             TextMessage msg = cmdMsg.geTextMessage();
    964             waitForUsersResponse = msg.responseNeeded;
    965             if (mStkContext[slotId].lastSelectedItem != null) {
    966                 msg.title = mStkContext[slotId].lastSelectedItem;
    967             } else if (mStkContext[slotId].mMainCmd != null){
    968                 if (!getResources().getBoolean(R.bool.show_menu_title_only_on_menu)) {
    969                     msg.title = mStkContext[slotId].mMainCmd.getMenu().title;
    970                 }
    971             }
    972             //If we receive a low priority Display Text and the device is
    973             // not displaying any STK related activity and the screen is not idle
    974             // ( that is, device is in an interactive state), then send a screen busy
    975             // terminal response. Otherwise display the message. The existing
    976             // displayed message shall be updated with the new display text
    977             // proactive command (Refer to ETSI TS 102 384 section 27.22.4.1.4.4.2).
    978             if (!(msg.isHighPriority || mStkContext[slotId].mMenuIsVisible
    979                     || mStkContext[slotId].mDisplayTextDlgIsVisibile || isTopOfStack())) {
    980                 if(!isScreenIdle()) {
    981                     CatLog.d(LOG_TAG, "Screen is not idle");
    982                     sendScreenBusyResponse(slotId);
    983                 } else {
    984                     launchTextDialog(slotId);
    985                 }
    986             } else {
    987                 launchTextDialog(slotId);
    988             }
    989             break;
    990         case SELECT_ITEM:
    991             CatLog.d(LOG_TAG, "SELECT_ITEM +");
    992             mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mCurrentCmd;
    993             mStkContext[slotId].mCurrentMenu = cmdMsg.getMenu();
    994             launchMenuActivity(cmdMsg.getMenu(), slotId);
    995             break;
    996         case SET_UP_MENU:
    997             mStkContext[slotId].mCmdInProgress = false;
    998             mStkContext[slotId].mMainCmd = mStkContext[slotId].mCurrentCmd;
    999             mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mCurrentCmd;
   1000             mStkContext[slotId].mCurrentMenu = cmdMsg.getMenu();
   1001             CatLog.d(LOG_TAG, "SET_UP_MENU [" + removeMenu(slotId) + "]");
   1002 
   1003             if (removeMenu(slotId)) {
   1004                 int i = 0;
   1005                 CatLog.d(LOG_TAG, "removeMenu() - Uninstall App");
   1006                 mStkContext[slotId].mCurrentMenu = null;
   1007                 mStkContext[slotId].mMainCmd = null;
   1008                 //Check other setup menu state. If all setup menu are removed, uninstall apk.
   1009                 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) {
   1010                     if (i != slotId
   1011                             && (mStkContext[i].mSetupMenuState == STATE_UNKNOWN
   1012                             || mStkContext[i].mSetupMenuState == STATE_EXIST)) {
   1013                         CatLog.d(LOG_TAG, "Not Uninstall App:" + i + ","
   1014                                 + mStkContext[i].mSetupMenuState);
   1015                         break;
   1016                     }
   1017                 }
   1018                 if (i == mSimCount) {
   1019                     StkAppInstaller.unInstall(mContext);
   1020                 }
   1021             } else {
   1022                 CatLog.d(LOG_TAG, "install App");
   1023                 StkAppInstaller.install(mContext);
   1024             }
   1025             if (mStkContext[slotId].mMenuIsVisible) {
   1026                 launchMenuActivity(null, slotId);
   1027             }
   1028             break;
   1029         case GET_INPUT:
   1030         case GET_INKEY:
   1031             launchInputActivity(slotId);
   1032             break;
   1033         case SET_UP_IDLE_MODE_TEXT:
   1034             waitForUsersResponse = false;
   1035             mStkContext[slotId].mIdleModeTextCmd = mStkContext[slotId].mCurrentCmd;
   1036             TextMessage idleModeText = mStkContext[slotId].mCurrentCmd.geTextMessage();
   1037             if (idleModeText == null || TextUtils.isEmpty(idleModeText.text)) {
   1038                 cancelIdleText(slotId);
   1039             }
   1040             mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd;
   1041             if (mStkContext[slotId].mIdleModeTextCmd != null) {
   1042                 if (mStkContext[slotId].mIdleModeTextVisible || isScreenIdle()) {
   1043                     CatLog.d(this, "set up idle mode");
   1044                     launchIdleText(slotId);
   1045                 } else {
   1046                     registerProcessObserver();
   1047                 }
   1048             }
   1049             break;
   1050         case SEND_DTMF:
   1051         case SEND_SMS:
   1052         case SEND_SS:
   1053         case SEND_USSD:
   1054         case GET_CHANNEL_STATUS:
   1055             waitForUsersResponse = false;
   1056             launchEventMessage(slotId);
   1057             break;
   1058         case LAUNCH_BROWSER:
   1059             // The device setup process should not be interrupted by launching browser.
   1060             if (Settings.Global.getInt(mContext.getContentResolver(),
   1061                     Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
   1062                 CatLog.d(this, "The command is not performed if the setup has not been completed.");
   1063                 sendScreenBusyResponse(slotId);
   1064                 break;
   1065             }
   1066 
   1067             /* Check if Carrier would not want to launch browser */
   1068             if (getBooleanCarrierConfig(mContext,
   1069                     CarrierConfigManager.KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL)) {
   1070                 CatLog.d(this, "Browser is not launched as per carrier.");
   1071                 sendResponse(RES_ID_DONE, slotId, true);
   1072                 break;
   1073             }
   1074 
   1075             mStkContext[slotId].mBrowserSettings =
   1076                     mStkContext[slotId].mCurrentCmd.getBrowserSettings();
   1077             if (!isUrlAvailableToLaunchBrowser(mStkContext[slotId].mBrowserSettings)) {
   1078                 CatLog.d(this, "Browser url property is not set - send error");
   1079                 sendResponse(RES_ID_ERROR, slotId, true);
   1080             } else {
   1081                 TextMessage alphaId = mStkContext[slotId].mCurrentCmd.geTextMessage();
   1082                 if ((alphaId == null) || TextUtils.isEmpty(alphaId.text)) {
   1083                     // don't need user confirmation in this case
   1084                     // just launch the browser or spawn a new tab
   1085                     CatLog.d(this, "user confirmation is not currently needed.\n" +
   1086                             "supressing confirmation dialogue and confirming silently...");
   1087                     mStkContext[slotId].launchBrowser = true;
   1088                     sendResponse(RES_ID_CONFIRM, slotId, true);
   1089                 } else {
   1090                     launchConfirmationDialog(alphaId, slotId);
   1091                 }
   1092             }
   1093             break;
   1094         case SET_UP_CALL:
   1095             TextMessage mesg = mStkContext[slotId].mCurrentCmd.getCallSettings().confirmMsg;
   1096             if((mesg != null) && (mesg.text == null || mesg.text.length() == 0)) {
   1097                 mesg.text = getResources().getString(R.string.default_setup_call_msg);
   1098             }
   1099             CatLog.d(this, "SET_UP_CALL mesg.text " + mesg.text);
   1100             launchConfirmationDialog(mesg, slotId);
   1101             break;
   1102         case PLAY_TONE:
   1103             handlePlayTone(slotId);
   1104             break;
   1105         case OPEN_CHANNEL:
   1106             launchOpenChannelDialog(slotId);
   1107             break;
   1108         case CLOSE_CHANNEL:
   1109         case RECEIVE_DATA:
   1110         case SEND_DATA:
   1111             TextMessage m = mStkContext[slotId].mCurrentCmd.geTextMessage();
   1112 
   1113             if ((m != null) && (m.text == null)) {
   1114                 switch(cmdMsg.getCmdType()) {
   1115                 case CLOSE_CHANNEL:
   1116                     m.text = getResources().getString(R.string.default_close_channel_msg);
   1117                     break;
   1118                 case RECEIVE_DATA:
   1119                     m.text = getResources().getString(R.string.default_receive_data_msg);
   1120                     break;
   1121                 case SEND_DATA:
   1122                     m.text = getResources().getString(R.string.default_send_data_msg);
   1123                     break;
   1124                 }
   1125             }
   1126             /*
   1127              * Display indication in the form of a toast to the user if required.
   1128              */
   1129             launchEventMessage(slotId);
   1130             break;
   1131         case SET_UP_EVENT_LIST:
   1132             replaceEventList(slotId);
   1133             if (isScreenIdle()) {
   1134                 CatLog.d(this," Check if IDLE_SCREEN_AVAILABLE_EVENT is present in List");
   1135                 checkForSetupEvent(IDLE_SCREEN_AVAILABLE_EVENT, null, slotId);
   1136             }
   1137             break;
   1138         }
   1139 
   1140         if (!waitForUsersResponse) {
   1141             if (mStkContext[slotId].mCmdsQ.size() != 0) {
   1142                 callDelayedMsg(slotId);
   1143             } else {
   1144                 mStkContext[slotId].mCmdInProgress = false;
   1145             }
   1146         }
   1147     }
   1148 
   1149     private void handleCmdResponse(Bundle args, int slotId) {
   1150         CatLog.d(LOG_TAG, "handleCmdResponse, sim id: " + slotId);
   1151         if (mStkContext[slotId].mCurrentCmd == null) {
   1152             return;
   1153         }
   1154 
   1155         if (mStkService[slotId] == null) {
   1156             mStkService[slotId] = CatService.getInstance(slotId);
   1157             if (mStkService[slotId] == null) {
   1158                 // This should never happen (we should be responding only to a message
   1159                 // that arrived from StkService). It has to exist by this time
   1160                 CatLog.d(LOG_TAG, "Exception! mStkService is null when we need to send response.");
   1161                 throw new RuntimeException("mStkService is null when we need to send response");
   1162             }
   1163         }
   1164 
   1165         CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentCmd);
   1166 
   1167         // set result code
   1168         boolean helpRequired = args.getBoolean(HELP, false);
   1169         boolean confirmed    = false;
   1170 
   1171         switch(args.getInt(RES_ID)) {
   1172         case RES_ID_MENU_SELECTION:
   1173             CatLog.d(LOG_TAG, "MENU_SELECTION=" + mStkContext[slotId].
   1174                     mCurrentMenuCmd.getCmdType());
   1175             int menuSelection = args.getInt(MENU_SELECTION);
   1176             switch(mStkContext[slotId].mCurrentMenuCmd.getCmdType()) {
   1177             case SET_UP_MENU:
   1178             case SELECT_ITEM:
   1179                 mStkContext[slotId].lastSelectedItem = getItemName(menuSelection, slotId);
   1180                 if (helpRequired) {
   1181                     resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
   1182                 } else {
   1183                     resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ?
   1184                             ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK);
   1185                 }
   1186                 resMsg.setMenuSelection(menuSelection);
   1187                 break;
   1188             }
   1189             break;
   1190         case RES_ID_INPUT:
   1191             CatLog.d(LOG_TAG, "RES_ID_INPUT");
   1192             String input = args.getString(INPUT);
   1193             if (input != null && (null != mStkContext[slotId].mCurrentCmd.geInput()) &&
   1194                     (mStkContext[slotId].mCurrentCmd.geInput().yesNo)) {
   1195                 boolean yesNoSelection = input
   1196                         .equals(StkInputActivity.YES_STR_RESPONSE);
   1197                 resMsg.setYesNo(yesNoSelection);
   1198             } else {
   1199                 if (helpRequired) {
   1200                     resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
   1201                 } else {
   1202                     resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ?
   1203                             ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK);
   1204                     resMsg.setInput(input);
   1205                 }
   1206             }
   1207             break;
   1208         case RES_ID_CONFIRM:
   1209             CatLog.d(this, "RES_ID_CONFIRM");
   1210             confirmed = args.getBoolean(CONFIRMATION);
   1211             switch (mStkContext[slotId].mCurrentCmd.getCmdType()) {
   1212             case DISPLAY_TEXT:
   1213                 if (confirmed) {
   1214                     resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ?
   1215                             ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK);
   1216                 } else {
   1217                     resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER);
   1218                 }
   1219                 break;
   1220             case LAUNCH_BROWSER:
   1221                 resMsg.setResultCode(confirmed ? ResultCode.OK
   1222                         : ResultCode.UICC_SESSION_TERM_BY_USER);
   1223                 if (confirmed) {
   1224                     mStkContext[slotId].launchBrowser = true;
   1225                     mStkContext[slotId].mBrowserSettings =
   1226                             mStkContext[slotId].mCurrentCmd.getBrowserSettings();
   1227                 }
   1228                 break;
   1229             case SET_UP_CALL:
   1230                 resMsg.setResultCode(ResultCode.OK);
   1231                 resMsg.setConfirmation(confirmed);
   1232                 if (confirmed) {
   1233                     launchEventMessage(slotId,
   1234                             mStkContext[slotId].mCurrentCmd.getCallSettings().callMsg);
   1235                 }
   1236                 break;
   1237             }
   1238             break;
   1239         case RES_ID_DONE:
   1240             resMsg.setResultCode(ResultCode.OK);
   1241             break;
   1242         case RES_ID_BACKWARD:
   1243             CatLog.d(LOG_TAG, "RES_ID_BACKWARD");
   1244             resMsg.setResultCode(ResultCode.BACKWARD_MOVE_BY_USER);
   1245             break;
   1246         case RES_ID_END_SESSION:
   1247             CatLog.d(LOG_TAG, "RES_ID_END_SESSION");
   1248             resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER);
   1249             break;
   1250         case RES_ID_TIMEOUT:
   1251             CatLog.d(LOG_TAG, "RES_ID_TIMEOUT");
   1252             // GCF test-case 27.22.4.1.1 Expected Sequence 1.5 (DISPLAY TEXT,
   1253             // Clear message after delay, successful) expects result code OK.
   1254             // If the command qualifier specifies no user response is required
   1255             // then send OK instead of NO_RESPONSE_FROM_USER
   1256             if ((mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1257                     AppInterface.CommandType.DISPLAY_TEXT.value())
   1258                     && (mStkContext[slotId].mCurrentCmd.geTextMessage().userClear == false)) {
   1259                 resMsg.setResultCode(ResultCode.OK);
   1260             } else {
   1261                 resMsg.setResultCode(ResultCode.NO_RESPONSE_FROM_USER);
   1262             }
   1263             break;
   1264         case RES_ID_CHOICE:
   1265             int choice = args.getInt(CHOICE);
   1266             CatLog.d(this, "User Choice=" + choice);
   1267             switch (choice) {
   1268                 case YES:
   1269                     resMsg.setResultCode(ResultCode.OK);
   1270                     confirmed = true;
   1271                     break;
   1272                 case NO:
   1273                     resMsg.setResultCode(ResultCode.USER_NOT_ACCEPT);
   1274                     break;
   1275             }
   1276 
   1277             if (mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1278                     AppInterface.CommandType.OPEN_CHANNEL.value()) {
   1279                 resMsg.setConfirmation(confirmed);
   1280             }
   1281             break;
   1282         case RES_ID_ERROR:
   1283             CatLog.d(LOG_TAG, "RES_ID_ERROR");
   1284             switch (mStkContext[slotId].mCurrentCmd.getCmdType()) {
   1285             case LAUNCH_BROWSER:
   1286                 resMsg.setResultCode(ResultCode.LAUNCH_BROWSER_ERROR);
   1287                 break;
   1288             }
   1289             break;
   1290         default:
   1291             CatLog.d(LOG_TAG, "Unknown result id");
   1292             return;
   1293         }
   1294 
   1295         switch (args.getInt(RES_ID)) {
   1296             case RES_ID_MENU_SELECTION:
   1297             case RES_ID_INPUT:
   1298             case RES_ID_CONFIRM:
   1299             case RES_ID_CHOICE:
   1300             case RES_ID_BACKWARD:
   1301             case RES_ID_END_SESSION:
   1302                 mStkContext[slotId].mNoResponseFromUser = false;
   1303                 break;
   1304             case RES_ID_TIMEOUT:
   1305                 cancelNotificationOnKeyguard(slotId);
   1306                 mStkContext[slotId].mNoResponseFromUser = true;
   1307                 break;
   1308             default:
   1309                 // The other IDs cannot be used to judge if there is no response from user.
   1310                 break;
   1311         }
   1312 
   1313         if (null != mStkContext[slotId].mCurrentCmd &&
   1314                 null != mStkContext[slotId].mCurrentCmd.getCmdType()) {
   1315             CatLog.d(LOG_TAG, "handleCmdResponse- cmdName[" +
   1316                     mStkContext[slotId].mCurrentCmd.getCmdType().name() + "]");
   1317         }
   1318         mStkService[slotId].onCmdResponse(resMsg);
   1319     }
   1320 
   1321     /**
   1322      * Returns 0 or FLAG_ACTIVITY_NO_USER_ACTION, 0 means the user initiated the action.
   1323      *
   1324      * @param userAction If the userAction is yes then we always return 0 otherwise
   1325      * mMenuIsVisible is used to determine what to return. If mMenuIsVisible is true
   1326      * then we are the foreground app and we'll return 0 as from our perspective a
   1327      * user action did cause. If it's false than we aren't the foreground app and
   1328      * FLAG_ACTIVITY_NO_USER_ACTION is returned.
   1329      *
   1330      * @return 0 or FLAG_ACTIVITY_NO_USER_ACTION
   1331      */
   1332     private int getFlagActivityNoUserAction(InitiatedByUserAction userAction, int slotId) {
   1333         return ((userAction == InitiatedByUserAction.yes) | mStkContext[slotId].mMenuIsVisible)
   1334                 ? 0 : Intent.FLAG_ACTIVITY_NO_USER_ACTION;
   1335     }
   1336     /**
   1337      * This method is used for cleaning up pending instances in stack.
   1338      */
   1339     private void cleanUpInstanceStackBySlot(int slotId) {
   1340         Activity activity = mStkContext[slotId].getPendingActivityInstance();
   1341         Activity dialog = mStkContext[slotId].getPendingDialogInstance();
   1342         CatLog.d(LOG_TAG, "cleanUpInstanceStackBySlot slotId: " + slotId);
   1343         if (mStkContext[slotId].mCurrentCmd == null) {
   1344             CatLog.d(LOG_TAG, "current cmd is null.");
   1345             return;
   1346         }
   1347         if (activity != null) {
   1348             CatLog.d(LOG_TAG, "current cmd type: " +
   1349                     mStkContext[slotId].mCurrentCmd.getCmdType());
   1350             if (mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1351                     AppInterface.CommandType.GET_INPUT.value() ||
   1352                     mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1353                     AppInterface.CommandType.GET_INKEY.value()) {
   1354                 mStkContext[slotId].mIsInputPending = true;
   1355             } else if (mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1356                     AppInterface.CommandType.SET_UP_MENU.value() ||
   1357                     mStkContext[slotId].mCurrentCmd.getCmdType().value() ==
   1358                     AppInterface.CommandType.SELECT_ITEM.value()) {
   1359                 mStkContext[slotId].mIsMenuPending = true;
   1360             } else {
   1361             }
   1362             CatLog.d(LOG_TAG, "finish pending activity.");
   1363             activity.finish();
   1364             mStkContext[slotId].mActivityInstance = null;
   1365         }
   1366         if (dialog != null) {
   1367             CatLog.d(LOG_TAG, "finish pending dialog.");
   1368             mStkContext[slotId].mIsDialogPending = true;
   1369             dialog.finish();
   1370             mStkContext[slotId].mDialogInstance = null;
   1371         }
   1372     }
   1373     /**
   1374      * This method is used for restoring pending instances from stack.
   1375      */
   1376     private void restoreInstanceFromStackBySlot(int slotId) {
   1377         AppInterface.CommandType cmdType = mStkContext[slotId].mCurrentCmd.getCmdType();
   1378 
   1379         CatLog.d(LOG_TAG, "restoreInstanceFromStackBySlot cmdType : " + cmdType);
   1380         switch(cmdType) {
   1381             case GET_INPUT:
   1382             case GET_INKEY:
   1383                 launchInputActivity(slotId);
   1384                 //Set mMenuIsVisible to true for showing main menu for
   1385                 //following session end command.
   1386                 mStkContext[slotId].mMenuIsVisible = true;
   1387             break;
   1388             case DISPLAY_TEXT:
   1389                 launchTextDialog(slotId);
   1390             break;
   1391             case LAUNCH_BROWSER:
   1392                 launchConfirmationDialog(mStkContext[slotId].mCurrentCmd.geTextMessage(),
   1393                         slotId);
   1394             break;
   1395             case OPEN_CHANNEL:
   1396                 launchOpenChannelDialog(slotId);
   1397             break;
   1398             case SET_UP_CALL:
   1399                 launchConfirmationDialog(mStkContext[slotId].mCurrentCmd.getCallSettings().
   1400                         confirmMsg, slotId);
   1401             break;
   1402             case SET_UP_MENU:
   1403             case SELECT_ITEM:
   1404                 launchMenuActivity(null, slotId);
   1405             break;
   1406         default:
   1407             break;
   1408         }
   1409     }
   1410 
   1411     @Override
   1412     public void startActivity(Intent intent) {
   1413         int slotId = intent.getIntExtra(SLOT_ID, SubscriptionManager.INVALID_SIM_SLOT_INDEX);
   1414         // Close the dialog displayed for DISPLAY TEXT command with an immediate response object
   1415         // before new dialog is displayed.
   1416         if (SubscriptionManager.isValidSlotIndex(slotId)) {
   1417             Activity dialog = mStkContext[slotId].getImmediateDialogInstance();
   1418             if (dialog != null) {
   1419                 CatLog.d(LOG_TAG, "finish dialog for immediate response.");
   1420                 dialog.finish();
   1421             }
   1422         }
   1423         super.startActivity(intent);
   1424     }
   1425 
   1426     private void launchMenuActivity(Menu menu, int slotId) {
   1427         Intent newIntent = new Intent(Intent.ACTION_VIEW);
   1428         String targetActivity = STK_MENU_ACTIVITY_NAME;
   1429         String uriString = STK_MENU_URI + System.currentTimeMillis();
   1430         //Set unique URI to create a new instance of activity for different slotId.
   1431         Uri uriData = Uri.parse(uriString);
   1432 
   1433         CatLog.d(LOG_TAG, "launchMenuActivity, slotId: " + slotId + " , " +
   1434                 uriData.toString() + " , " + mStkContext[slotId].mOpCode + ", "
   1435                 + mStkContext[slotId].mMenuState);
   1436         newIntent.setClassName(PACKAGE_NAME, targetActivity);
   1437         int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK;
   1438 
   1439         if (menu == null) {
   1440             // We assume this was initiated by the user pressing the tool kit icon
   1441             intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.yes, slotId);
   1442             //If the last pending menu is secondary menu, "STATE" should be "STATE_SECONDARY".
   1443             //Otherwise, it should be "STATE_MAIN".
   1444             if (mStkContext[slotId].mOpCode == OP_LAUNCH_APP &&
   1445                     mStkContext[slotId].mMenuState == StkMenuActivity.STATE_SECONDARY) {
   1446                 newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY);
   1447             } else {
   1448                 newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN);
   1449                 mStkContext[slotId].mMenuState = StkMenuActivity.STATE_MAIN;
   1450             }
   1451         } else {
   1452             // We don't know and we'll let getFlagActivityNoUserAction decide.
   1453             intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId);
   1454             newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY);
   1455             mStkContext[slotId].mMenuState = StkMenuActivity.STATE_SECONDARY;
   1456         }
   1457         newIntent.putExtra(SLOT_ID, slotId);
   1458         newIntent.setData(uriData);
   1459         newIntent.setFlags(intentFlags);
   1460         startActivity(newIntent);
   1461     }
   1462 
   1463     private void launchInputActivity(int slotId) {
   1464         Intent newIntent = new Intent(Intent.ACTION_VIEW);
   1465         String targetActivity = STK_INPUT_ACTIVITY_NAME;
   1466         String uriString = STK_INPUT_URI + System.currentTimeMillis();
   1467         //Set unique URI to create a new instance of activity for different slotId.
   1468         Uri uriData = Uri.parse(uriString);
   1469         Input input = mStkContext[slotId].mCurrentCmd.geInput();
   1470 
   1471         CatLog.d(LOG_TAG, "launchInputActivity, slotId: " + slotId);
   1472         newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1473                             | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId));
   1474         newIntent.setClassName(PACKAGE_NAME, targetActivity);
   1475         newIntent.putExtra("INPUT", input);
   1476         newIntent.putExtra(SLOT_ID, slotId);
   1477         newIntent.setData(uriData);
   1478 
   1479         if (input != null) {
   1480             notifyUserIfNecessary(slotId, input.text);
   1481         }
   1482         startActivity(newIntent);
   1483     }
   1484 
   1485     private void launchTextDialog(int slotId) {
   1486         CatLog.d(LOG_TAG, "launchTextDialog, slotId: " + slotId);
   1487         Intent newIntent = new Intent();
   1488         String targetActivity = STK_DIALOG_ACTIVITY_NAME;
   1489         int action = getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId);
   1490         String uriString = STK_DIALOG_URI + System.currentTimeMillis();
   1491         //Set unique URI to create a new instance of activity for different slotId.
   1492         Uri uriData = Uri.parse(uriString);
   1493         TextMessage textMessage = mStkContext[slotId].mCurrentCmd.geTextMessage();
   1494 
   1495         newIntent.setClassName(PACKAGE_NAME, targetActivity);
   1496         newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1497                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
   1498                 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId));
   1499         newIntent.setData(uriData);
   1500         newIntent.putExtra("TEXT", textMessage);
   1501         newIntent.putExtra(SLOT_ID, slotId);
   1502 
   1503         if (textMessage != null) {
   1504             notifyUserIfNecessary(slotId, textMessage.text);
   1505         }
   1506         startActivity(newIntent);
   1507         // For display texts with immediate response, send the terminal response
   1508         // immediately. responseNeeded will be false, if display text command has
   1509         // the immediate response tlv.
   1510         if (!mStkContext[slotId].mCurrentCmd.geTextMessage().responseNeeded) {
   1511             sendResponse(RES_ID_CONFIRM, slotId, true);
   1512         }
   1513     }
   1514 
   1515     private void notifyUserIfNecessary(int slotId, String message) {
   1516         createAllChannels();
   1517 
   1518         if (mStkContext[slotId].mNoResponseFromUser) {
   1519             // No response from user was observed in the current session.
   1520             // Do nothing in that case in order to avoid turning on the screen again and again
   1521             // when the card repeatedly sends the same command in its retry procedure.
   1522             return;
   1523         }
   1524 
   1525         PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
   1526 
   1527         if (((KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE)).isKeyguardLocked()) {
   1528             // Display the notification on the keyguard screen
   1529             // if user cannot see the message from the card right now because of it.
   1530             // The notification can be dismissed if user removed the keyguard screen.
   1531             launchNotificationOnKeyguard(slotId, message);
   1532         } else if (!(pm.isInteractive() && isTopOfStack())) {
   1533             // User might be doing something but it is not related to the SIM Toolkit.
   1534             // Play the tone and do vibration in order to attract user's attention.
   1535             // User will see the input screen or the dialog soon in this case.
   1536             NotificationChannel channel = mNotificationManager
   1537                     .getNotificationChannel(STK_NOTIFICATION_CHANNEL_ID);
   1538             Uri uri = channel.getSound();
   1539             if (uri != null && !Uri.EMPTY.equals(uri)
   1540                     && (NotificationManager.IMPORTANCE_LOW) < channel.getImportance()) {
   1541                 RingtoneManager.getRingtone(getApplicationContext(), uri).play();
   1542             }
   1543             long[] pattern = channel.getVibrationPattern();
   1544             if (pattern != null && channel.shouldVibrate()) {
   1545                 ((Vibrator) this.getSystemService(Context.VIBRATOR_SERVICE))
   1546                         .vibrate(pattern, -1);
   1547             }
   1548         }
   1549 
   1550         // Turn on the screen.
   1551         PowerManager.WakeLock wakelock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK
   1552                 | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, LOG_TAG);
   1553         wakelock.acquire();
   1554         wakelock.release();
   1555     }
   1556 
   1557     private void launchNotificationOnKeyguard(int slotId, String message) {
   1558         Notification.Builder builder = new Notification.Builder(this, STK_NOTIFICATION_CHANNEL_ID);
   1559 
   1560         builder.setStyle(new Notification.BigTextStyle(builder).bigText(message));
   1561         builder.setContentText(message);
   1562 
   1563         Menu menu = getMainMenu(slotId);
   1564         if (menu == null || TextUtils.isEmpty(menu.title)) {
   1565             builder.setContentTitle(getResources().getString(R.string.app_name));
   1566         } else {
   1567             builder.setContentTitle(menu.title);
   1568         }
   1569 
   1570         builder.setSmallIcon(com.android.internal.R.drawable.stat_notify_sim_toolkit);
   1571         builder.setOngoing(true);
   1572         builder.setOnlyAlertOnce(true);
   1573         builder.setColor(getResources().getColor(
   1574                 com.android.internal.R.color.system_notification_accent_color));
   1575 
   1576         registerUserPresentReceiver();
   1577         mNotificationManager.notify(getNotificationId(NOTIFICATION_ON_KEYGUARD, slotId),
   1578                 builder.build());
   1579         mStkContext[slotId].mNotificationOnKeyguard = true;
   1580     }
   1581 
   1582     private void cancelNotificationOnKeyguard(int slotId) {
   1583         mNotificationManager.cancel(getNotificationId(NOTIFICATION_ON_KEYGUARD, slotId));
   1584         mStkContext[slotId].mNotificationOnKeyguard = false;
   1585         unregisterUserPresentReceiver(slotId);
   1586     }
   1587 
   1588     private synchronized void registerUserPresentReceiver() {
   1589         if (mUserPresentReceiver == null) {
   1590             mUserPresentReceiver = new BroadcastReceiver() {
   1591                 @Override public void onReceive(Context context, Intent intent) {
   1592                     if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
   1593                         for (int slot = 0; slot < mSimCount; slot++) {
   1594                             cancelNotificationOnKeyguard(slot);
   1595                         }
   1596                     }
   1597                 }
   1598             };
   1599             registerReceiver(mUserPresentReceiver, new IntentFilter(Intent.ACTION_USER_PRESENT));
   1600         }
   1601     }
   1602 
   1603     private synchronized void unregisterUserPresentReceiver(int slotId) {
   1604         if (mUserPresentReceiver != null) {
   1605             for (int slot = PhoneConstants.SIM_ID_1; slot < mSimCount; slot++) {
   1606                 if (slot != slotId) {
   1607                     if (mStkContext[slot].mNotificationOnKeyguard) {
   1608                         // The broadcast receiver is still necessary for other SIM card.
   1609                         return;
   1610                     }
   1611                 }
   1612             }
   1613             unregisterReceiver(mUserPresentReceiver);
   1614             mUserPresentReceiver = null;
   1615         }
   1616     }
   1617 
   1618     private int getNotificationId(int notificationType, int slotId) {
   1619         return getNotificationId(slotId) + (notificationType * mSimCount);
   1620     }
   1621 
   1622     public boolean isStkDialogActivated(Context context) {
   1623         String stkDialogActivity = "com.android.stk.StkDialogActivity";
   1624         boolean activated = false;
   1625         final ActivityManager am = (ActivityManager) context.getSystemService(
   1626                 Context.ACTIVITY_SERVICE);
   1627         String topActivity = am.getRunningTasks(1).get(0).topActivity.getClassName();
   1628 
   1629         CatLog.d(LOG_TAG, "isStkDialogActivated: " + topActivity);
   1630         if (topActivity.equals(stkDialogActivity)) {
   1631             activated = true;
   1632         }
   1633         CatLog.d(LOG_TAG, "activated : " + activated);
   1634         return activated;
   1635     }
   1636 
   1637     private void replaceEventList(int slotId) {
   1638         if (mStkContext[slotId].mSetupEventListSettings != null) {
   1639             for (int current : mStkContext[slotId].mSetupEventListSettings.eventList) {
   1640                 if (current != INVALID_SETUP_EVENT) {
   1641                     // Cancel the event notification if it is not listed in the new event list.
   1642                     if ((mStkContext[slotId].mCurrentCmd.getSetEventList() == null)
   1643                             || !findEvent(current, mStkContext[slotId].mCurrentCmd
   1644                             .getSetEventList().eventList)) {
   1645                         unregisterEvent(current, slotId);
   1646                     }
   1647                 }
   1648             }
   1649         }
   1650         mStkContext[slotId].mSetupEventListSettings
   1651                 = mStkContext[slotId].mCurrentCmd.getSetEventList();
   1652         mStkContext[slotId].mCurrentSetupEventCmd = mStkContext[slotId].mCurrentCmd;
   1653         mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd;
   1654         registerEvents(slotId);
   1655     }
   1656 
   1657     private boolean findEvent(int event, int[] eventList) {
   1658         for (int content : eventList) {
   1659             if (content == event) return true;
   1660         }
   1661         return false;
   1662     }
   1663 
   1664     private void unregisterEvent(int event, int slotId) {
   1665         switch (event) {
   1666             case USER_ACTIVITY_EVENT:
   1667                 unregisterUserActivityReceiver();
   1668                 break;
   1669             case IDLE_SCREEN_AVAILABLE_EVENT:
   1670                 unregisterProcessObserver(AppInterface.CommandType.SET_UP_EVENT_LIST, slotId);
   1671                 break;
   1672             case LANGUAGE_SELECTION_EVENT:
   1673             default:
   1674                 break;
   1675         }
   1676     }
   1677 
   1678     private void registerEvents(int slotId) {
   1679         if (mStkContext[slotId].mSetupEventListSettings == null) {
   1680             return;
   1681         }
   1682         for (int event : mStkContext[slotId].mSetupEventListSettings.eventList) {
   1683             switch (event) {
   1684                 case USER_ACTIVITY_EVENT:
   1685                     registerUserActivityReceiver();
   1686                     break;
   1687                 case IDLE_SCREEN_AVAILABLE_EVENT:
   1688                     registerProcessObserver();
   1689                     break;
   1690                 case LANGUAGE_SELECTION_EVENT:
   1691                 default:
   1692                     break;
   1693             }
   1694         }
   1695     }
   1696 
   1697     private synchronized void registerUserActivityReceiver() {
   1698         if (mUserActivityReceiver == null) {
   1699             mUserActivityReceiver = new BroadcastReceiver() {
   1700                 @Override public void onReceive(Context context, Intent intent) {
   1701                     if (WindowManagerPolicyConstants.ACTION_USER_ACTIVITY_NOTIFICATION.equals(
   1702                             intent.getAction())) {
   1703                         Message message = mServiceHandler.obtainMessage();
   1704                         message.arg1 = OP_USER_ACTIVITY;
   1705                         mServiceHandler.sendMessage(message);
   1706                         unregisterUserActivityReceiver();
   1707                     }
   1708                 }
   1709             };
   1710             registerReceiver(mUserActivityReceiver, new IntentFilter(
   1711                     WindowManagerPolicyConstants.ACTION_USER_ACTIVITY_NOTIFICATION));
   1712             try {
   1713                 IWindowManager wm = IWindowManager.Stub.asInterface(
   1714                         ServiceManager.getService(Context.WINDOW_SERVICE));
   1715                 wm.requestUserActivityNotification();
   1716             } catch (RemoteException e) {
   1717                 CatLog.e(this, "failed to init WindowManager:" + e);
   1718             }
   1719         }
   1720     }
   1721 
   1722     private synchronized void unregisterUserActivityReceiver() {
   1723         if (mUserActivityReceiver != null) {
   1724             unregisterReceiver(mUserActivityReceiver);
   1725             mUserActivityReceiver = null;
   1726         }
   1727     }
   1728 
   1729     private synchronized void registerProcessObserver() {
   1730         if (mProcessObserver == null) {
   1731             try {
   1732                 IProcessObserver.Stub observer = new IProcessObserver.Stub() {
   1733                     @Override
   1734                     public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
   1735                         if (isScreenIdle()) {
   1736                             Message message = mServiceHandler.obtainMessage();
   1737                             message.arg1 = OP_IDLE_SCREEN;
   1738                             mServiceHandler.sendMessage(message);
   1739                             unregisterProcessObserver();
   1740                         }
   1741                     }
   1742 
   1743                     @Override
   1744                     public void onProcessDied(int pid, int uid) {
   1745                     }
   1746                 };
   1747                 ActivityManagerNative.getDefault().registerProcessObserver(observer);
   1748                 mProcessObserver = observer;
   1749             } catch (RemoteException e) {
   1750                 CatLog.d(this, "Failed to register the process observer");
   1751             }
   1752         }
   1753     }
   1754 
   1755     private void unregisterProcessObserver(AppInterface.CommandType command, int slotId) {
   1756         // Check if there is any pending command which still needs the process observer
   1757         // except for the current command and slot.
   1758         for (int slot = PhoneConstants.SIM_ID_1; slot < mSimCount; slot++) {
   1759             if (command != AppInterface.CommandType.SET_UP_IDLE_MODE_TEXT || slot != slotId) {
   1760                 if (mStkContext[slot].mIdleModeTextCmd != null
   1761                         && !mStkContext[slot].mIdleModeTextVisible) {
   1762                     // Keep the process observer registered
   1763                     // as there is an idle mode text which has not been visible yet.
   1764                     return;
   1765                 }
   1766             }
   1767             if (command != AppInterface.CommandType.SET_UP_EVENT_LIST || slot != slotId) {
   1768                 if (mStkContext[slot].mSetupEventListSettings != null) {
   1769                     if (findEvent(IDLE_SCREEN_AVAILABLE_EVENT,
   1770                                 mStkContext[slot].mSetupEventListSettings.eventList)) {
   1771                         // Keep the process observer registered
   1772                         // as there is a SIM card which still want IDLE SCREEN AVAILABLE event.
   1773                         return;
   1774                     }
   1775                 }
   1776             }
   1777         }
   1778         unregisterProcessObserver();
   1779     }
   1780 
   1781     private synchronized void unregisterProcessObserver() {
   1782         if (mProcessObserver != null) {
   1783             try {
   1784                 ActivityManagerNative.getDefault().unregisterProcessObserver(mProcessObserver);
   1785                 mProcessObserver = null;
   1786             } catch (RemoteException e) {
   1787                 CatLog.d(this, "Failed to unregister the process observer");
   1788             }
   1789         }
   1790     }
   1791 
   1792     private void sendSetUpEventResponse(int event, byte[] addedInfo, int slotId) {
   1793         CatLog.d(this, "sendSetUpEventResponse: event : " + event + "slotId = " + slotId);
   1794 
   1795         if (mStkContext[slotId].mCurrentSetupEventCmd == null){
   1796             CatLog.e(this, "mCurrentSetupEventCmd is null");
   1797             return;
   1798         }
   1799 
   1800         CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentSetupEventCmd);
   1801 
   1802         resMsg.setResultCode(ResultCode.OK);
   1803         resMsg.setEventDownload(event, addedInfo);
   1804 
   1805         mStkService[slotId].onCmdResponse(resMsg);
   1806     }
   1807 
   1808     private void checkForSetupEvent(int event, Bundle args, int slotId) {
   1809         boolean eventPresent = false;
   1810         byte[] addedInfo = null;
   1811         CatLog.d(this, "Event :" + event);
   1812 
   1813         if (mStkContext[slotId].mSetupEventListSettings != null) {
   1814             /* Checks if the event is present in the EventList updated by last
   1815              * SetupEventList Proactive Command */
   1816             for (int i : mStkContext[slotId].mSetupEventListSettings.eventList) {
   1817                  if (event == i) {
   1818                      eventPresent =  true;
   1819                      break;
   1820                  }
   1821             }
   1822 
   1823             /* If Event is present send the response to ICC */
   1824             if (eventPresent == true) {
   1825                 CatLog.d(this, " Event " + event + "exists in the EventList");
   1826 
   1827                 switch (event) {
   1828                     case USER_ACTIVITY_EVENT:
   1829                     case IDLE_SCREEN_AVAILABLE_EVENT:
   1830                         sendSetUpEventResponse(event, addedInfo, slotId);
   1831                         removeSetUpEvent(event, slotId);
   1832                         break;
   1833                     case LANGUAGE_SELECTION_EVENT:
   1834                         String language =  mContext
   1835                                 .getResources().getConfiguration().locale.getLanguage();
   1836                         CatLog.d(this, "language: " + language);
   1837                         // Each language code is a pair of alpha-numeric characters.
   1838                         // Each alpha-numeric character shall be coded on one byte
   1839                         // using the SMS default 7-bit coded alphabet
   1840                         addedInfo = GsmAlphabet.stringToGsm8BitPacked(language);
   1841                         sendSetUpEventResponse(event, addedInfo, slotId);
   1842                         break;
   1843                     default:
   1844                         break;
   1845                 }
   1846             } else {
   1847                 CatLog.e(this, " Event does not exist in the EventList");
   1848             }
   1849         } else {
   1850             CatLog.e(this, "SetupEventList is not received. Ignoring the event: " + event);
   1851         }
   1852     }
   1853 
   1854     private void removeSetUpEvent(int event, int slotId) {
   1855         CatLog.d(this, "Remove Event :" + event);
   1856 
   1857         if (mStkContext[slotId].mSetupEventListSettings != null) {
   1858             /*
   1859              * Make new  Eventlist without the event
   1860              */
   1861             for (int i = 0; i < mStkContext[slotId].mSetupEventListSettings.eventList.length; i++) {
   1862                 if (event == mStkContext[slotId].mSetupEventListSettings.eventList[i]) {
   1863                     mStkContext[slotId].mSetupEventListSettings.eventList[i] = INVALID_SETUP_EVENT;
   1864 
   1865                     switch (event) {
   1866                         case USER_ACTIVITY_EVENT:
   1867                             // The broadcast receiver can be unregistered
   1868                             // as the event has already been sent to the card.
   1869                             unregisterUserActivityReceiver();
   1870                             break;
   1871                         case IDLE_SCREEN_AVAILABLE_EVENT:
   1872                             // The process observer can be unregistered
   1873                             // as the idle screen has already been available.
   1874                             unregisterProcessObserver();
   1875                             break;
   1876                         default:
   1877                             break;
   1878                     }
   1879                     break;
   1880                 }
   1881             }
   1882         }
   1883     }
   1884 
   1885     private void launchEventMessage(int slotId) {
   1886         launchEventMessage(slotId, mStkContext[slotId].mCurrentCmd.geTextMessage());
   1887     }
   1888 
   1889     private void launchEventMessage(int slotId, TextMessage msg) {
   1890         if (msg == null || msg.text == null || (msg.text != null && msg.text.length() == 0)) {
   1891             CatLog.d(LOG_TAG, "launchEventMessage return");
   1892             return;
   1893         }
   1894 
   1895         Toast toast = new Toast(mContext.getApplicationContext());
   1896         LayoutInflater inflate = (LayoutInflater) mContext
   1897                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   1898         View v = inflate.inflate(R.layout.stk_event_msg, null);
   1899         TextView tv = (TextView) v
   1900                 .findViewById(com.android.internal.R.id.message);
   1901         ImageView iv = (ImageView) v
   1902                 .findViewById(com.android.internal.R.id.icon);
   1903         if (msg.icon != null) {
   1904             iv.setImageBitmap(msg.icon);
   1905         } else {
   1906             iv.setVisibility(View.GONE);
   1907         }
   1908         /* In case of 'self explanatory' stkapp should display the specified
   1909          * icon in proactive command (but not the alpha string).
   1910          * If icon is non-self explanatory and if the icon could not be displayed
   1911          * then alpha string or text data should be displayed
   1912          * Ref: ETSI 102.223,section 6.5.4
   1913          */
   1914         if (mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ||
   1915                 msg.icon == null || !msg.iconSelfExplanatory) {
   1916             tv.setText(msg.text);
   1917         }
   1918 
   1919         toast.setView(v);
   1920         toast.setDuration(Toast.LENGTH_LONG);
   1921         toast.setGravity(Gravity.BOTTOM, 0, 0);
   1922         toast.show();
   1923     }
   1924 
   1925     private void launchConfirmationDialog(TextMessage msg, int slotId) {
   1926         msg.title = mStkContext[slotId].lastSelectedItem;
   1927         Intent newIntent = new Intent();
   1928         String targetActivity = STK_DIALOG_ACTIVITY_NAME;
   1929         String uriString = STK_DIALOG_URI + System.currentTimeMillis();
   1930         //Set unique URI to create a new instance of activity for different slotId.
   1931         Uri uriData = Uri.parse(uriString);
   1932 
   1933         newIntent.setClassName(this, targetActivity);
   1934         newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   1935                 | Intent.FLAG_ACTIVITY_NO_HISTORY
   1936                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
   1937                 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId));
   1938         newIntent.putExtra("TEXT", msg);
   1939         newIntent.putExtra(SLOT_ID, slotId);
   1940         newIntent.setData(uriData);
   1941         startActivity(newIntent);
   1942     }
   1943 
   1944     private void launchBrowser(BrowserSettings settings) {
   1945         if (settings == null) {
   1946             return;
   1947         }
   1948 
   1949         Uri data = null;
   1950         String url;
   1951         if (settings.url == null) {
   1952             // if the command did not contain a URL,
   1953             // launch the browser to the default homepage.
   1954             CatLog.d(this, "no url data provided by proactive command." +
   1955                        " launching browser with stk default URL ... ");
   1956             url = SystemProperties.get(STK_BROWSER_DEFAULT_URL_SYSPROP,
   1957                     "http://www.google.com");
   1958         } else {
   1959             CatLog.d(this, "launch browser command has attached url = " + settings.url);
   1960             url = settings.url;
   1961         }
   1962 
   1963         if (url.startsWith("http://") || url.startsWith("https://")) {
   1964             data = Uri.parse(url);
   1965             CatLog.d(this, "launching browser with url = " + url);
   1966         } else {
   1967             String modifiedUrl = "http://" + url;
   1968             data = Uri.parse(modifiedUrl);
   1969             CatLog.d(this, "launching browser with modified url = " + modifiedUrl);
   1970         }
   1971 
   1972         Intent intent = new Intent(Intent.ACTION_VIEW);
   1973         intent.setData(data);
   1974         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1975         switch (settings.mode) {
   1976         case USE_EXISTING_BROWSER:
   1977             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
   1978             break;
   1979         case LAUNCH_NEW_BROWSER:
   1980             intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
   1981             break;
   1982         case LAUNCH_IF_NOT_ALREADY_LAUNCHED:
   1983             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
   1984             break;
   1985         }
   1986         // start browser activity
   1987         startActivity(intent);
   1988         // a small delay, let the browser start, before processing the next command.
   1989         // this is good for scenarios where a related DISPLAY TEXT command is
   1990         // followed immediately.
   1991         try {
   1992             Thread.sleep(3000);
   1993         } catch (InterruptedException e) {}
   1994     }
   1995 
   1996     private void cancelIdleText(int slotId) {
   1997         unregisterProcessObserver(AppInterface.CommandType.SET_UP_IDLE_MODE_TEXT, slotId);
   1998         mNotificationManager.cancel(getNotificationId(slotId));
   1999         mStkContext[slotId].mIdleModeTextCmd = null;
   2000         mStkContext[slotId].mIdleModeTextVisible = false;
   2001     }
   2002 
   2003     private void launchIdleText(int slotId) {
   2004         TextMessage msg = mStkContext[slotId].mIdleModeTextCmd.geTextMessage();
   2005 
   2006         if (msg != null && !TextUtils.isEmpty(msg.text)) {
   2007             CatLog.d(LOG_TAG, "launchIdleText - text[" + msg.text
   2008                     + "] iconSelfExplanatory[" + msg.iconSelfExplanatory
   2009                     + "] icon[" + msg.icon + "], sim id: " + slotId);
   2010             CatLog.d(LOG_TAG, "Add IdleMode text");
   2011             PendingIntent pendingIntent = PendingIntent.getService(mContext, 0,
   2012                     new Intent(mContext, StkAppService.class), 0);
   2013             createAllChannels();
   2014             final Notification.Builder notificationBuilder = new Notification.Builder(
   2015                     StkAppService.this, STK_NOTIFICATION_CHANNEL_ID);
   2016             if (mStkContext[slotId].mMainCmd != null &&
   2017                     mStkContext[slotId].mMainCmd.getMenu() != null) {
   2018                 notificationBuilder.setContentTitle(mStkContext[slotId].mMainCmd.getMenu().title);
   2019             } else {
   2020                 notificationBuilder.setContentTitle("");
   2021             }
   2022             notificationBuilder
   2023                     .setSmallIcon(com.android.internal.R.drawable.stat_notify_sim_toolkit);
   2024             notificationBuilder.setContentIntent(pendingIntent);
   2025             notificationBuilder.setOngoing(true);
   2026             notificationBuilder.setOnlyAlertOnce(true);
   2027             // Set text and icon for the status bar and notification body.
   2028             if (mStkContext[slotId].mIdleModeTextCmd.hasIconLoadFailed() ||
   2029                     !msg.iconSelfExplanatory) {
   2030                 notificationBuilder.setContentText(msg.text);
   2031                 notificationBuilder.setTicker(msg.text);
   2032                 notificationBuilder.setStyle(new Notification.BigTextStyle(notificationBuilder)
   2033                         .bigText(msg.text));
   2034             }
   2035             if (msg.icon != null) {
   2036                 notificationBuilder.setLargeIcon(msg.icon);
   2037             } else {
   2038                 Bitmap bitmapIcon = BitmapFactory.decodeResource(StkAppService.this
   2039                     .getResources().getSystem(),
   2040                     com.android.internal.R.drawable.stat_notify_sim_toolkit);
   2041                 notificationBuilder.setLargeIcon(bitmapIcon);
   2042             }
   2043             notificationBuilder.setColor(mContext.getResources().getColor(
   2044                     com.android.internal.R.color.system_notification_accent_color));
   2045             mNotificationManager.notify(getNotificationId(slotId), notificationBuilder.build());
   2046             mStkContext[slotId].mIdleModeTextVisible = true;
   2047         }
   2048     }
   2049 
   2050     /** Creates the notification channel and registers it with NotificationManager.
   2051      * If a channel with the same ID is already registered, NotificationManager will
   2052      * ignore this call.
   2053      */
   2054     private void createAllChannels() {
   2055         NotificationChannel notificationChannel = new NotificationChannel(
   2056                 STK_NOTIFICATION_CHANNEL_ID,
   2057                 getResources().getString(R.string.stk_channel_name),
   2058                 NotificationManager.IMPORTANCE_DEFAULT);
   2059 
   2060         notificationChannel.enableVibration(true);
   2061         notificationChannel.setVibrationPattern(VIBRATION_PATTERN);
   2062 
   2063         mNotificationManager.createNotificationChannel(notificationChannel);
   2064     }
   2065 
   2066     private void launchToneDialog(int slotId) {
   2067         Intent newIntent = new Intent(this, ToneDialog.class);
   2068         String uriString = STK_TONE_URI + slotId;
   2069         Uri uriData = Uri.parse(uriString);
   2070         //Set unique URI to create a new instance of activity for different slotId.
   2071         CatLog.d(LOG_TAG, "launchToneDialog, slotId: " + slotId);
   2072         newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   2073                 | Intent.FLAG_ACTIVITY_NO_HISTORY
   2074                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
   2075                 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId));
   2076         newIntent.putExtra("TEXT", mStkContext[slotId].mCurrentCmd.geTextMessage());
   2077         newIntent.putExtra("TONE", mStkContext[slotId].mCurrentCmd.getToneSettings());
   2078         newIntent.putExtra(SLOT_ID, slotId);
   2079         newIntent.setData(uriData);
   2080         startActivity(newIntent);
   2081     }
   2082 
   2083     private void handlePlayTone(int slotId) {
   2084         TextMessage toneMsg = mStkContext[slotId].mCurrentCmd.geTextMessage();
   2085 
   2086         boolean showUser = true;
   2087         boolean displayDialog = true;
   2088         Resources resource = Resources.getSystem();
   2089         try {
   2090             displayDialog = !resource.getBoolean(
   2091                     com.android.internal.R.bool.config_stkNoAlphaUsrCnf);
   2092         } catch (NotFoundException e) {
   2093             displayDialog = true;
   2094         }
   2095 
   2096         // As per the spec 3GPP TS 11.14, 6.4.5. Play Tone.
   2097         // If there is no alpha identifier tlv present, UE may show the
   2098         // user information. 'config_stkNoAlphaUsrCnf' value will decide
   2099         // whether to show it or not.
   2100         // If alpha identifier tlv is present and its data is null, play only tone
   2101         // without showing user any information.
   2102         // Alpha Id is Present, but the text data is null.
   2103         if ((toneMsg.text != null ) && (toneMsg.text.equals(""))) {
   2104             CatLog.d(this, "Alpha identifier data is null, play only tone");
   2105             showUser = false;
   2106         }
   2107         // Alpha Id is not present AND we need to show info to the user.
   2108         if (toneMsg.text == null && displayDialog) {
   2109             CatLog.d(this, "toneMsg.text " + toneMsg.text
   2110                     + " Starting ToneDialog activity with default message.");
   2111             toneMsg.text = getResources().getString(R.string.default_tone_dialog_msg);
   2112             showUser = true;
   2113         }
   2114         // Dont show user info, if config setting is true.
   2115         if (toneMsg.text == null && !displayDialog) {
   2116             CatLog.d(this, "config value stkNoAlphaUsrCnf is true");
   2117             showUser = false;
   2118         }
   2119 
   2120         CatLog.d(this, "toneMsg.text: " + toneMsg.text + "showUser: " +showUser +
   2121                 "displayDialog: " +displayDialog);
   2122         playTone(showUser, slotId);
   2123     }
   2124 
   2125     private void playTone(boolean showUserInfo, int slotId) {
   2126         // Start playing tone and vibration
   2127         ToneSettings settings = mStkContext[slotId].mCurrentCmd.getToneSettings();
   2128         if (null == settings) {
   2129             CatLog.d(this, "null settings, not playing tone.");
   2130             return;
   2131         }
   2132 
   2133         mVibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE);
   2134         mTonePlayer = new TonePlayer();
   2135         mTonePlayer.play(settings.tone);
   2136         int timeout = StkApp.calculateDurationInMilis(settings.duration);
   2137         if (timeout == 0) {
   2138             timeout = StkApp.TONE_DEFAULT_TIMEOUT;
   2139         }
   2140 
   2141         Message msg = mServiceHandler.obtainMessage();
   2142         msg.arg1 = OP_STOP_TONE;
   2143         msg.arg2 = slotId;
   2144         msg.obj = (Integer)(showUserInfo ? 1 : 0);
   2145         msg.what = STOP_TONE_WHAT;
   2146         mServiceHandler.sendMessageDelayed(msg, timeout);
   2147         if (settings.vibrate) {
   2148             mVibrator.vibrate(timeout);
   2149         }
   2150 
   2151         // Start Tone dialog Activity to show user the information.
   2152         if (showUserInfo) {
   2153             Intent newIntent = new Intent(sInstance, ToneDialog.class);
   2154             String uriString = STK_TONE_URI + slotId;
   2155             Uri uriData = Uri.parse(uriString);
   2156             newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   2157                     | Intent.FLAG_ACTIVITY_NO_HISTORY
   2158                     | Intent.FLAG_ACTIVITY_SINGLE_TOP
   2159                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
   2160                     | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId));
   2161             newIntent.putExtra("TEXT", mStkContext[slotId].mCurrentCmd.geTextMessage());
   2162             newIntent.putExtra(SLOT_ID, slotId);
   2163             newIntent.setData(uriData);
   2164             startActivity(newIntent);
   2165         }
   2166     }
   2167 
   2168     private void finishToneDialogActivity() {
   2169         Intent finishIntent = new Intent(FINISH_TONE_ACTIVITY_ACTION);
   2170         sendBroadcast(finishIntent);
   2171     }
   2172 
   2173     private void handleStopTone(Message msg, int slotId) {
   2174         int resId = 0;
   2175 
   2176         // Stop the play tone in following cases:
   2177         // 1.OP_STOP_TONE: play tone timer expires.
   2178         // 2.STOP_TONE_USER: user pressed the back key.
   2179         if (msg.arg1 == OP_STOP_TONE) {
   2180             resId = RES_ID_DONE;
   2181             // Dismiss Tone dialog, after finishing off playing the tone.
   2182             int finishActivity = (Integer) msg.obj;
   2183             if (finishActivity == 1) finishToneDialogActivity();
   2184         } else if (msg.arg1 == OP_STOP_TONE_USER) {
   2185             resId = RES_ID_END_SESSION;
   2186         }
   2187 
   2188         sendResponse(resId, slotId, true);
   2189         mServiceHandler.removeMessages(STOP_TONE_WHAT);
   2190         if (mTonePlayer != null)  {
   2191             mTonePlayer.stop();
   2192             mTonePlayer.release();
   2193             mTonePlayer = null;
   2194         }
   2195         if (mVibrator != null) {
   2196             mVibrator.cancel();
   2197             mVibrator = null;
   2198         }
   2199     }
   2200 
   2201     private void launchOpenChannelDialog(final int slotId) {
   2202         TextMessage msg = mStkContext[slotId].mCurrentCmd.geTextMessage();
   2203         if (msg == null) {
   2204             CatLog.d(LOG_TAG, "msg is null, return here");
   2205             return;
   2206         }
   2207 
   2208         msg.title = getResources().getString(R.string.stk_dialog_title);
   2209         if (msg.text == null) {
   2210             msg.text = getResources().getString(R.string.default_open_channel_msg);
   2211         }
   2212 
   2213         final AlertDialog dialog = new AlertDialog.Builder(mContext)
   2214                     .setIconAttribute(android.R.attr.alertDialogIcon)
   2215                     .setTitle(msg.title)
   2216                     .setMessage(msg.text)
   2217                     .setCancelable(false)
   2218                     .setPositiveButton(getResources().getString(R.string.stk_dialog_accept),
   2219                                        new DialogInterface.OnClickListener() {
   2220                         public void onClick(DialogInterface dialog, int which) {
   2221                             Bundle args = new Bundle();
   2222                             args.putInt(RES_ID, RES_ID_CHOICE);
   2223                             args.putInt(CHOICE, YES);
   2224                             Message message = mServiceHandler.obtainMessage();
   2225                             message.arg1 = OP_RESPONSE;
   2226                             message.arg2 = slotId;
   2227                             message.obj = args;
   2228                             mServiceHandler.sendMessage(message);
   2229                         }
   2230                     })
   2231                     .setNegativeButton(getResources().getString(R.string.stk_dialog_reject),
   2232                                        new DialogInterface.OnClickListener() {
   2233                         public void onClick(DialogInterface dialog, int which) {
   2234                             Bundle args = new Bundle();
   2235                             args.putInt(RES_ID, RES_ID_CHOICE);
   2236                             args.putInt(CHOICE, NO);
   2237                             Message message = mServiceHandler.obtainMessage();
   2238                             message.arg1 = OP_RESPONSE;
   2239                             message.arg2 = slotId;
   2240                             message.obj = args;
   2241                             mServiceHandler.sendMessage(message);
   2242                         }
   2243                     })
   2244                     .create();
   2245 
   2246         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   2247         if (!mContext.getResources().getBoolean(
   2248                 com.android.internal.R.bool.config_sf_slowBlur)) {
   2249             dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
   2250         }
   2251 
   2252         dialog.show();
   2253     }
   2254 
   2255     private void launchTransientEventMessage(int slotId) {
   2256         TextMessage msg = mStkContext[slotId].mCurrentCmd.geTextMessage();
   2257         if (msg == null) {
   2258             CatLog.d(LOG_TAG, "msg is null, return here");
   2259             return;
   2260         }
   2261 
   2262         msg.title = getResources().getString(R.string.stk_dialog_title);
   2263 
   2264         final AlertDialog dialog = new AlertDialog.Builder(mContext)
   2265                     .setIconAttribute(android.R.attr.alertDialogIcon)
   2266                     .setTitle(msg.title)
   2267                     .setMessage(msg.text)
   2268                     .setCancelable(false)
   2269                     .setPositiveButton(getResources().getString(android.R.string.ok),
   2270                                        new DialogInterface.OnClickListener() {
   2271                         public void onClick(DialogInterface dialog, int which) {
   2272                         }
   2273                     })
   2274                     .create();
   2275 
   2276         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   2277         if (!mContext.getResources().getBoolean(
   2278                 com.android.internal.R.bool.config_sf_slowBlur)) {
   2279             dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
   2280         }
   2281 
   2282         dialog.show();
   2283     }
   2284 
   2285     private int getNotificationId(int slotId) {
   2286         int notifyId = STK_NOTIFICATION_ID;
   2287         if (slotId >= 0 && slotId < mSimCount) {
   2288             notifyId += slotId;
   2289         } else {
   2290             CatLog.d(LOG_TAG, "invalid slotId: " + slotId);
   2291         }
   2292         CatLog.d(LOG_TAG, "getNotificationId, slotId: " + slotId + ", notifyId: " + notifyId);
   2293         return notifyId;
   2294     }
   2295 
   2296     private String getItemName(int itemId, int slotId) {
   2297         Menu menu = mStkContext[slotId].mCurrentCmd.getMenu();
   2298         if (menu == null) {
   2299             return null;
   2300         }
   2301         for (Item item : menu.items) {
   2302             if (item.id == itemId) {
   2303                 return item.text;
   2304             }
   2305         }
   2306         return null;
   2307     }
   2308 
   2309     private boolean removeMenu(int slotId) {
   2310         try {
   2311             if (mStkContext[slotId].mCurrentMenu.items.size() == 1 &&
   2312                 mStkContext[slotId].mCurrentMenu.items.get(0) == null) {
   2313                 mStkContext[slotId].mSetupMenuState = STATE_NOT_EXIST;
   2314                 return true;
   2315             }
   2316         } catch (NullPointerException e) {
   2317             CatLog.d(LOG_TAG, "Unable to get Menu's items size");
   2318             mStkContext[slotId].mSetupMenuState = STATE_NOT_EXIST;
   2319             return true;
   2320         }
   2321         mStkContext[slotId].mSetupMenuState = STATE_EXIST;
   2322         return false;
   2323     }
   2324 
   2325     StkContext getStkContext(int slotId) {
   2326         if (slotId >= 0 && slotId < mSimCount) {
   2327             return mStkContext[slotId];
   2328         } else {
   2329             CatLog.d(LOG_TAG, "invalid slotId: " + slotId);
   2330             return null;
   2331         }
   2332     }
   2333 
   2334     private void handleAlphaNotify(Bundle args) {
   2335         String alphaString = args.getString(AppInterface.ALPHA_STRING);
   2336 
   2337         CatLog.d(this, "Alpha string received from card: " + alphaString);
   2338         Toast toast = Toast.makeText(sInstance, alphaString, Toast.LENGTH_LONG);
   2339         toast.setGravity(Gravity.TOP, 0, 0);
   2340         toast.show();
   2341     }
   2342 
   2343     private boolean isUrlAvailableToLaunchBrowser(BrowserSettings settings) {
   2344         String url = SystemProperties.get(STK_BROWSER_DEFAULT_URL_SYSPROP, "");
   2345         if (url == "" && settings.url == null) {
   2346             return false;
   2347         }
   2348         return true;
   2349     }
   2350 }
   2351