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