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