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