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