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