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