1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.stk;
18 
19 import android.app.ActivityManager;
20 import android.app.ActivityManager.RunningTaskInfo;
21 import android.app.AlertDialog;
22 import android.app.Notification;
23 import android.app.NotificationManager;
24 import android.app.PendingIntent;
25 import android.app.Service;
26 import android.app.Activity;
27 import android.app.ActivityManager;
28 import android.app.ActivityManager.RecentTaskInfo;
29 import android.app.ActivityManager.RunningAppProcessInfo;
30 import android.content.Context;
31 import android.content.DialogInterface;
32 import android.content.Intent;
33 import android.content.res.Configuration;
34 import android.graphics.Bitmap;
35 import android.graphics.BitmapFactory;
36 import android.net.Uri;
37 import android.os.Bundle;
38 import android.os.Handler;
39 import android.os.IBinder;
40 import android.os.Looper;
41 import android.os.Message;
42 import android.os.PowerManager;
43 import android.provider.Settings;
44 import android.telephony.TelephonyManager;
45 import android.view.Gravity;
46 import android.view.LayoutInflater;
47 import android.view.View;
48 import android.view.Window;
49 import android.view.WindowManager;
50 import android.widget.ImageView;
51 import android.widget.RemoteViews;
52 import android.widget.TextView;
53 import android.widget.Toast;
54 import android.content.BroadcastReceiver;
55 import android.content.IntentFilter;
56 import android.content.pm.ApplicationInfo;
57 import android.content.pm.PackageManager.NameNotFoundException;
58 
59 import com.android.internal.telephony.cat.AppInterface;
60 import com.android.internal.telephony.cat.LaunchBrowserMode;
61 import com.android.internal.telephony.cat.Menu;
62 import com.android.internal.telephony.cat.Item;
63 import com.android.internal.telephony.cat.Input;
64 import com.android.internal.telephony.cat.ResultCode;
65 import com.android.internal.telephony.cat.CatCmdMessage;
66 import com.android.internal.telephony.cat.CatCmdMessage.BrowserSettings;
67 import com.android.internal.telephony.cat.CatCmdMessage.SetupEventListSettings;
68 import com.android.internal.telephony.cat.CatLog;
69 import com.android.internal.telephony.cat.CatResponseMessage;
70 import com.android.internal.telephony.cat.TextMessage;
71 import com.android.internal.telephony.uicc.IccRefreshResponse;
72 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
73 import com.android.internal.telephony.PhoneConstants;
74 import com.android.internal.telephony.TelephonyIntents;
75 import com.android.internal.telephony.IccCardConstants;
76 import com.android.internal.telephony.uicc.UiccController;
77 import com.android.internal.telephony.GsmAlphabet;
78 import com.android.internal.telephony.cat.CatService;
79 
80 import java.util.LinkedList;
81 import java.lang.System;
82 import java.util.List;
83 
84 import static com.android.internal.telephony.cat.CatCmdMessage.
85                    SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT;
86 import 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  */
94 public 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;
setPendingActivityInstance(Activity act)125         final synchronized void setPendingActivityInstance(Activity act) {
126             CatLog.d(this, "setPendingActivityInstance act : " + mSlotId + ", " + act);
127             callSetActivityInstMsg(OP_SET_ACT_INST, mSlotId, act);
128         }
getPendingActivityInstance()129         final synchronized Activity getPendingActivityInstance() {
130             CatLog.d(this, "getPendingActivityInstance act : " + mSlotId + ", " +
131                     mActivityInstance);
132             return mActivityInstance;
133         }
setPendingDialogInstance(Activity act)134         final synchronized void setPendingDialogInstance(Activity act) {
135             CatLog.d(this, "setPendingDialogInstance act : " + mSlotId + ", " + act);
136             callSetActivityInstMsg(OP_SET_DAL_INST, mSlotId, act);
137         }
getPendingDialogInstance()138         final synchronized Activity getPendingDialogInstance() {
139             CatLog.d(this, "getPendingDialogInstance act : " + mSlotId + ", " +
140                     mDialogInstance);
141             return mDialogInstance;
142         }
setMainActivityInstance(Activity act)143         final synchronized void setMainActivityInstance(Activity act) {
144             CatLog.d(this, "setMainActivityInstance act : " + mSlotId + ", " + act);
145             callSetActivityInstMsg(OP_SET_MAINACT_INST, mSlotId, act);
146         }
getMainActivityInstance()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 
DelayedCmd(int id, CatCmdMessage msg, int slotId)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
onCreate()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
onStart(Intent intent, int startId)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
onDestroy()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
onBind(Intent intent)363     public IBinder onBind(Intent intent) {
364         return null;
365     }
366 
run()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      */
indicateMenuVisibility(boolean visibility, int slotId)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      */
setDisplayTextDlgVisibility(boolean visibility, int slotId)388     void setDisplayTextDlgVisibility(boolean visibility, int slotId) {
389         if (slotId >= 0 && slotId < mSimCount) {
390             mStkContext[slotId].mDisplayTextDlgIsVisibile = visibility;
391         }
392     }
393 
isInputPending(int slotId)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 
isMenuPending(int slotId)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 
isDialogPending(int slotId)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      */
getMenu(int slotId)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      */
getMainMenu(int slotId)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      */
getInstance()446     static StkAppService getInstance() {
447         return sInstance;
448     }
449 
waitForLooper()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
handleMessage(Message msg)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 
handleCardStatusChangeAndIccRefresh(Bundle args, int slotId)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      */
isAllOtherCardsAbsent(int slotId)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      */
isScreenIdle()651     private boolean isScreenIdle() {
652         return (!mPowerManager.isInteractive());
653     }
654 
handleIdleScreen(int slotId)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 
sendScreenBusyResponse(int slotId)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 
sendResponse(int resId, int slotId, boolean confirm)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 
isCmdInteractive(CatCmdMessage cmd)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 
handleDelayedCmd(int slotId)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 
callDelayedMsg(int slotId)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 
callSetActivityInstMsg(int inst_type, int slotId, Object obj)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 
handleSessionEnd(int slotId)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
isTopOfStack()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 
handleCmd(CatCmdMessage cmdMsg, int slotId)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 
handleCmdResponse(Bundle args, int slotId)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      */
getFlagActivityNoUserAction(InitiatedByUserAction userAction, int slotId)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      */
cleanUpInstanceStackBySlot(int slotId)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      */
restoreInstanceFromStackBySlot(int slotId)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 
launchMenuActivity(Menu menu, int slotId)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 
launchInputActivity(int slotId)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 
launchTextDialog(int slotId)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 
isStkDialogActivated(Context context)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 
sendSetUpEventResponse(int event, byte[] addedInfo, int slotId)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 
checkForSetupEvent(int event, Bundle args, int slotId)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 
removeSetUpEvent(int event, int slotId)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 
launchEventMessage(int slotId)1386     private void launchEventMessage(int slotId) {
1387         launchEventMessage(slotId, mStkContext[slotId].mCurrentCmd.geTextMessage());
1388     }
1389 
launchEventMessage(int slotId, TextMessage msg)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 
launchConfirmationDialog(TextMessage msg, int slotId)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 
launchBrowser(BrowserSettings settings)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 
launchIdleText(int slotId)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 
launchToneDialog(int slotId)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 
launchOpenChannelDialog(int slotId)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 
launchTransientEventMessage(int slotId)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 
getNotificationId(int slotId)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 
getItemName(int itemId, int slotId)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 
removeMenu(int slotId)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 
getStkContext(int slotId)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 
handleAlphaNotify(Bundle args)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