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