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