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