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