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