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