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