1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.stk; 18 19 import android.app.ActivityManager; 20 import android.app.ActivityManager.RunningTaskInfo; 21 import android.app.AlertDialog; 22 import android.app.Notification; 23 import android.app.NotificationChannel; 24 import android.app.NotificationManager; 25 import android.app.PendingIntent; 26 import android.app.Service; 27 import android.app.Activity; 28 import android.content.Context; 29 import android.content.DialogInterface; 30 import android.content.Intent; 31 import android.graphics.Bitmap; 32 import android.graphics.BitmapFactory; 33 import android.net.Uri; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.IBinder; 37 import android.os.Looper; 38 import android.os.Message; 39 import android.os.PersistableBundle; 40 import android.os.PowerManager; 41 import android.os.SystemProperties; 42 import android.provider.Settings; 43 import android.telephony.CarrierConfigManager; 44 import android.telephony.TelephonyManager; 45 import android.text.TextUtils; 46 import android.view.Gravity; 47 import android.view.LayoutInflater; 48 import android.view.View; 49 import android.view.WindowManager; 50 import android.widget.ImageView; 51 import android.widget.TextView; 52 import android.widget.Toast; 53 import android.content.IntentFilter; 54 55 import com.android.internal.telephony.cat.AppInterface; 56 import com.android.internal.telephony.cat.LaunchBrowserMode; 57 import com.android.internal.telephony.cat.Menu; 58 import com.android.internal.telephony.cat.Item; 59 import com.android.internal.telephony.cat.ResultCode; 60 import com.android.internal.telephony.cat.CatCmdMessage; 61 import com.android.internal.telephony.cat.CatCmdMessage.BrowserSettings; 62 import com.android.internal.telephony.cat.CatCmdMessage.SetupEventListSettings; 63 import com.android.internal.telephony.cat.CatLog; 64 import com.android.internal.telephony.cat.CatResponseMessage; 65 import com.android.internal.telephony.cat.TextMessage; 66 import com.android.internal.telephony.uicc.IccRefreshResponse; 67 import com.android.internal.telephony.PhoneConstants; 68 import com.android.internal.telephony.GsmAlphabet; 69 import com.android.internal.telephony.cat.CatService; 70 71 import java.util.Iterator; 72 import java.util.LinkedList; 73 import java.lang.System; 74 import java.util.List; 75 76 import static com.android.internal.telephony.cat.CatCmdMessage. 77 SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT; 78 import static com.android.internal.telephony.cat.CatCmdMessage. 79 SetupEventListConstants.LANGUAGE_SELECTION_EVENT; 80 81 /** 82 * SIM toolkit application level service. Interacts with Telephopny messages, 83 * application's launch and user input from STK UI elements. 84 * 85 */ 86 public class StkAppService extends Service implements Runnable { 87 88 // members 89 protected class StkContext { 90 protected CatCmdMessage mMainCmd = null; 91 protected CatCmdMessage mCurrentCmd = null; 92 protected CatCmdMessage mCurrentMenuCmd = null; 93 protected Menu mCurrentMenu = null; 94 protected String lastSelectedItem = null; 95 protected boolean mMenuIsVisible = false; 96 protected boolean mIsInputPending = false; 97 protected boolean mIsMenuPending = false; 98 protected boolean mIsDialogPending = false; 99 protected boolean responseNeeded = true; 100 protected boolean launchBrowser = false; 101 protected BrowserSettings mBrowserSettings = null; 102 protected LinkedList<DelayedCmd> mCmdsQ = null; 103 protected boolean mCmdInProgress = false; 104 protected int mStkServiceState = STATE_UNKNOWN; 105 protected int mSetupMenuState = STATE_UNKNOWN; 106 protected int mMenuState = StkMenuActivity.STATE_INIT; 107 protected int mOpCode = -1; 108 private Activity mActivityInstance = null; 109 private Activity mDialogInstance = null; 110 private Activity mMainActivityInstance = null; 111 private int mSlotId = 0; 112 private SetupEventListSettings mSetupEventListSettings = null; 113 private boolean mClearSelectItem = false; 114 private boolean mDisplayTextDlgIsVisibile = false; 115 private CatCmdMessage mCurrentSetupEventCmd = null; 116 private CatCmdMessage mIdleModeTextCmd = null; setPendingActivityInstance(Activity act)117 final synchronized void setPendingActivityInstance(Activity act) { 118 CatLog.d(this, "setPendingActivityInstance act : " + mSlotId + ", " + act); 119 callSetActivityInstMsg(OP_SET_ACT_INST, mSlotId, act); 120 } getPendingActivityInstance()121 final synchronized Activity getPendingActivityInstance() { 122 CatLog.d(this, "getPendingActivityInstance act : " + mSlotId + ", " + 123 mActivityInstance); 124 return mActivityInstance; 125 } setPendingDialogInstance(Activity act)126 final synchronized void setPendingDialogInstance(Activity act) { 127 CatLog.d(this, "setPendingDialogInstance act : " + mSlotId + ", " + act); 128 callSetActivityInstMsg(OP_SET_DAL_INST, mSlotId, act); 129 } getPendingDialogInstance()130 final synchronized Activity getPendingDialogInstance() { 131 CatLog.d(this, "getPendingDialogInstance act : " + mSlotId + ", " + 132 mDialogInstance); 133 return mDialogInstance; 134 } setMainActivityInstance(Activity act)135 final synchronized void setMainActivityInstance(Activity act) { 136 CatLog.d(this, "setMainActivityInstance act : " + mSlotId + ", " + act); 137 callSetActivityInstMsg(OP_SET_MAINACT_INST, mSlotId, act); 138 } getMainActivityInstance()139 final synchronized Activity getMainActivityInstance() { 140 CatLog.d(this, "getMainActivityInstance act : " + mSlotId + ", " + 141 mMainActivityInstance); 142 return mMainActivityInstance; 143 } 144 } 145 146 private volatile Looper mServiceLooper; 147 private volatile ServiceHandler mServiceHandler; 148 private Context mContext = null; 149 private NotificationManager mNotificationManager = null; 150 static StkAppService sInstance = null; 151 private AppInterface[] mStkService = null; 152 private StkContext[] mStkContext = null; 153 private int mSimCount = 0; 154 private PowerManager mPowerManager = null; 155 private StkCmdReceiver mStkCmdReceiver = null; 156 157 // Used for setting FLAG_ACTIVITY_NO_USER_ACTION when 158 // creating an intent. 159 private enum InitiatedByUserAction { 160 yes, // The action was started via a user initiated action 161 unknown, // Not known for sure if user initated the action 162 } 163 164 // constants 165 static final String OPCODE = "op"; 166 static final String CMD_MSG = "cmd message"; 167 static final String RES_ID = "response id"; 168 static final String MENU_SELECTION = "menu selection"; 169 static final String INPUT = "input"; 170 static final String HELP = "help"; 171 static final String CONFIRMATION = "confirm"; 172 static final String CHOICE = "choice"; 173 static final String SLOT_ID = "SLOT_ID"; 174 static final String STK_CMD = "STK CMD"; 175 static final String STK_DIALOG_URI = "stk://com.android.stk/dialog/"; 176 static final String STK_MENU_URI = "stk://com.android.stk/menu/"; 177 static final String STK_INPUT_URI = "stk://com.android.stk/input/"; 178 static final String STK_TONE_URI = "stk://com.android.stk/tone/"; 179 180 // These below constants are used for SETUP_EVENT_LIST 181 static final String SETUP_EVENT_TYPE = "event"; 182 static final String SETUP_EVENT_CAUSE = "cause"; 183 184 // operations ids for different service functionality. 185 static final int OP_CMD = 1; 186 static final int OP_RESPONSE = 2; 187 static final int OP_LAUNCH_APP = 3; 188 static final int OP_END_SESSION = 4; 189 static final int OP_BOOT_COMPLETED = 5; 190 private static final int OP_DELAYED_MSG = 6; 191 static final int OP_CARD_STATUS_CHANGED = 7; 192 static final int OP_SET_ACT_INST = 8; 193 static final int OP_SET_DAL_INST = 9; 194 static final int OP_SET_MAINACT_INST = 10; 195 static final int OP_LOCALE_CHANGED = 11; 196 static final int OP_ALPHA_NOTIFY = 12; 197 static final int OP_IDLE_SCREEN = 13; 198 199 //Invalid SetupEvent 200 static final int INVALID_SETUP_EVENT = 0xFF; 201 202 // Response ids 203 static final int RES_ID_MENU_SELECTION = 11; 204 static final int RES_ID_INPUT = 12; 205 static final int RES_ID_CONFIRM = 13; 206 static final int RES_ID_DONE = 14; 207 static final int RES_ID_CHOICE = 15; 208 209 static final int RES_ID_TIMEOUT = 20; 210 static final int RES_ID_BACKWARD = 21; 211 static final int RES_ID_END_SESSION = 22; 212 static final int RES_ID_EXIT = 23; 213 214 static final int YES = 1; 215 static final int NO = 0; 216 217 static final int STATE_UNKNOWN = -1; 218 static final int STATE_NOT_EXIST = 0; 219 static final int STATE_EXIST = 1; 220 221 private static final String PACKAGE_NAME = "com.android.stk"; 222 private static final String STK_MENU_ACTIVITY_NAME = PACKAGE_NAME + ".StkMenuActivity"; 223 private static final String STK_INPUT_ACTIVITY_NAME = PACKAGE_NAME + ".StkInputActivity"; 224 private static final String STK_DIALOG_ACTIVITY_NAME = PACKAGE_NAME + ".StkDialogActivity"; 225 // Notification id used to display Idle Mode text in NotificationManager. 226 private static final int STK_NOTIFICATION_ID = 333; 227 // Notification channel containing all mobile service messages notifications. 228 private static final String STK_NOTIFICATION_CHANNEL_ID = "mobileServiceMessages"; 229 230 private static final String LOG_TAG = new Object(){}.getClass().getEnclosingClass().getName(); 231 232 // Inner class used for queuing telephony messages (proactive commands, 233 // session end) while the service is busy processing a previous message. 234 private class DelayedCmd { 235 // members 236 int id; 237 CatCmdMessage msg; 238 int slotId; 239 DelayedCmd(int id, CatCmdMessage msg, int slotId)240 DelayedCmd(int id, CatCmdMessage msg, int slotId) { 241 this.id = id; 242 this.msg = msg; 243 this.slotId = slotId; 244 } 245 } 246 247 // system property to set the STK specific default url for launch browser proactive cmds 248 private static final String STK_BROWSER_DEFAULT_URL_SYSPROP = "persist.radio.stk.default_url"; 249 250 @Override onCreate()251 public void onCreate() { 252 CatLog.d(LOG_TAG, "onCreate()+"); 253 // Initialize members 254 int i = 0; 255 mContext = getBaseContext(); 256 mSimCount = TelephonyManager.from(mContext).getSimCount(); 257 CatLog.d(LOG_TAG, "simCount: " + mSimCount); 258 mStkService = new AppInterface[mSimCount]; 259 mStkContext = new StkContext[mSimCount]; 260 mPowerManager = (PowerManager)getSystemService(Context.POWER_SERVICE); 261 mStkCmdReceiver = new StkCmdReceiver(); 262 registerReceiver(mStkCmdReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); 263 for (i = 0; i < mSimCount; i++) { 264 CatLog.d(LOG_TAG, "slotId: " + i); 265 mStkService[i] = CatService.getInstance(i); 266 mStkContext[i] = new StkContext(); 267 mStkContext[i].mSlotId = i; 268 mStkContext[i].mCmdsQ = new LinkedList<DelayedCmd>(); 269 } 270 271 Thread serviceThread = new Thread(null, this, "Stk App Service"); 272 serviceThread.start(); 273 mNotificationManager = (NotificationManager) mContext 274 .getSystemService(Context.NOTIFICATION_SERVICE); 275 sInstance = this; 276 } 277 278 @Override onStart(Intent intent, int startId)279 public void onStart(Intent intent, int startId) { 280 if (intent == null) { 281 CatLog.d(LOG_TAG, "StkAppService onStart intent is null so return"); 282 return; 283 } 284 285 Bundle args = intent.getExtras(); 286 if (args == null) { 287 CatLog.d(LOG_TAG, "StkAppService onStart args is null so return"); 288 return; 289 } 290 291 int op = args.getInt(OPCODE); 292 int slotId = 0; 293 int i = 0; 294 if (op != OP_BOOT_COMPLETED) { 295 slotId = args.getInt(SLOT_ID); 296 } 297 CatLog.d(LOG_TAG, "onStart sim id: " + slotId + ", op: " + op + ", *****"); 298 if ((slotId >= 0 && slotId < mSimCount) && mStkService[slotId] == null) { 299 mStkService[slotId] = CatService.getInstance(slotId); 300 if (mStkService[slotId] == null) { 301 CatLog.d(LOG_TAG, "mStkService is: " + mStkContext[slotId].mStkServiceState); 302 mStkContext[slotId].mStkServiceState = STATE_NOT_EXIST; 303 //Check other StkService state. 304 //If all StkServices are not available, stop itself and uninstall apk. 305 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) { 306 if (i != slotId 307 && (mStkService[i] != null) 308 && (mStkContext[i].mStkServiceState == STATE_UNKNOWN 309 || mStkContext[i].mStkServiceState == STATE_EXIST)) { 310 break; 311 } 312 } 313 } else { 314 mStkContext[slotId].mStkServiceState = STATE_EXIST; 315 } 316 if (i == mSimCount) { 317 stopSelf(); 318 StkAppInstaller.unInstall(mContext); 319 return; 320 } 321 } 322 323 waitForLooper(); 324 325 Message msg = mServiceHandler.obtainMessage(); 326 msg.arg1 = op; 327 msg.arg2 = slotId; 328 switch(msg.arg1) { 329 case OP_CMD: 330 msg.obj = args.getParcelable(CMD_MSG); 331 break; 332 case OP_RESPONSE: 333 case OP_CARD_STATUS_CHANGED: 334 case OP_LOCALE_CHANGED: 335 case OP_ALPHA_NOTIFY: 336 case OP_IDLE_SCREEN: 337 msg.obj = args; 338 /* falls through */ 339 case OP_LAUNCH_APP: 340 case OP_END_SESSION: 341 case OP_BOOT_COMPLETED: 342 break; 343 default: 344 return; 345 } 346 mServiceHandler.sendMessage(msg); 347 } 348 349 @Override onDestroy()350 public void onDestroy() { 351 CatLog.d(LOG_TAG, "onDestroy()"); 352 if (mStkCmdReceiver != null) { 353 unregisterReceiver(mStkCmdReceiver); 354 mStkCmdReceiver = null; 355 } 356 mPowerManager = null; 357 waitForLooper(); 358 mServiceLooper.quit(); 359 } 360 361 @Override onBind(Intent intent)362 public IBinder onBind(Intent intent) { 363 return null; 364 } 365 run()366 public void run() { 367 Looper.prepare(); 368 369 mServiceLooper = Looper.myLooper(); 370 mServiceHandler = new ServiceHandler(); 371 372 Looper.loop(); 373 } 374 375 /* 376 * Package api used by StkMenuActivity to indicate if its on the foreground. 377 */ indicateMenuVisibility(boolean visibility, int slotId)378 void indicateMenuVisibility(boolean visibility, int slotId) { 379 if (slotId >= 0 && slotId < mSimCount) { 380 mStkContext[slotId].mMenuIsVisible = visibility; 381 } 382 } 383 384 /* 385 * Package api used by StkDialogActivity to indicate if its on the foreground. 386 */ setDisplayTextDlgVisibility(boolean visibility, int slotId)387 void setDisplayTextDlgVisibility(boolean visibility, int slotId) { 388 if (slotId >= 0 && slotId < mSimCount) { 389 mStkContext[slotId].mDisplayTextDlgIsVisibile = visibility; 390 } 391 } 392 isInputPending(int slotId)393 boolean isInputPending(int slotId) { 394 if (slotId >= 0 && slotId < mSimCount) { 395 CatLog.d(LOG_TAG, "isInputFinishBySrv: " + mStkContext[slotId].mIsInputPending); 396 return mStkContext[slotId].mIsInputPending; 397 } 398 return false; 399 } 400 isMenuPending(int slotId)401 boolean isMenuPending(int slotId) { 402 if (slotId >= 0 && slotId < mSimCount) { 403 CatLog.d(LOG_TAG, "isMenuPending: " + mStkContext[slotId].mIsMenuPending); 404 return mStkContext[slotId].mIsMenuPending; 405 } 406 return false; 407 } 408 isDialogPending(int slotId)409 boolean isDialogPending(int slotId) { 410 if (slotId >= 0 && slotId < mSimCount) { 411 CatLog.d(LOG_TAG, "isDialogPending: " + mStkContext[slotId].mIsDialogPending); 412 return mStkContext[slotId].mIsDialogPending; 413 } 414 return false; 415 } 416 417 /* 418 * Package api used by StkMenuActivity to get its Menu parameter. 419 */ getMenu(int slotId)420 Menu getMenu(int slotId) { 421 CatLog.d(LOG_TAG, "StkAppService, getMenu, sim id: " + slotId); 422 if (slotId >=0 && slotId < mSimCount) { 423 return mStkContext[slotId].mCurrentMenu; 424 } else { 425 return null; 426 } 427 } 428 429 /* 430 * Package api used by StkMenuActivity to get its Main Menu parameter. 431 */ getMainMenu(int slotId)432 Menu getMainMenu(int slotId) { 433 CatLog.d(LOG_TAG, "StkAppService, getMainMenu, sim id: " + slotId); 434 if (slotId >=0 && slotId < mSimCount && (mStkContext[slotId].mMainCmd != null)) { 435 return mStkContext[slotId].mMainCmd.getMenu(); 436 } else { 437 return null; 438 } 439 } 440 441 /* 442 * Package api used by UI Activities and Dialogs to communicate directly 443 * with the service to deliver state information and parameters. 444 */ getInstance()445 static StkAppService getInstance() { 446 return sInstance; 447 } 448 waitForLooper()449 private void waitForLooper() { 450 while (mServiceHandler == null) { 451 synchronized (this) { 452 try { 453 wait(100); 454 } catch (InterruptedException e) { 455 } 456 } 457 } 458 } 459 460 private final class ServiceHandler extends Handler { 461 @Override handleMessage(Message msg)462 public void handleMessage(Message msg) { 463 if(null == msg) { 464 CatLog.d(LOG_TAG, "ServiceHandler handleMessage msg is null"); 465 return; 466 } 467 int opcode = msg.arg1; 468 int slotId = msg.arg2; 469 470 CatLog.d(LOG_TAG, "handleMessage opcode[" + opcode + "], sim id[" + slotId + "]"); 471 if (opcode == OP_CMD && msg.obj != null && 472 ((CatCmdMessage)msg.obj).getCmdType()!= null) { 473 CatLog.d(LOG_TAG, "cmdName[" + ((CatCmdMessage)msg.obj).getCmdType().name() + "]"); 474 } 475 mStkContext[slotId].mOpCode = opcode; 476 switch (opcode) { 477 case OP_LAUNCH_APP: 478 if (mStkContext[slotId].mMainCmd == null) { 479 CatLog.d(LOG_TAG, "mMainCmd is null"); 480 // nothing todo when no SET UP MENU command didn't arrive. 481 return; 482 } 483 CatLog.d(LOG_TAG, "handleMessage OP_LAUNCH_APP - mCmdInProgress[" + 484 mStkContext[slotId].mCmdInProgress + "]"); 485 486 //If there is a pending activity for the slot id, 487 //just finish it and create a new one to handle the pending command. 488 cleanUpInstanceStackBySlot(slotId); 489 490 CatLog.d(LOG_TAG, "Current cmd type: " + 491 mStkContext[slotId].mCurrentCmd.getCmdType()); 492 //Restore the last command from stack by slot id. 493 restoreInstanceFromStackBySlot(slotId); 494 break; 495 case OP_CMD: 496 CatLog.d(LOG_TAG, "[OP_CMD]"); 497 CatCmdMessage cmdMsg = (CatCmdMessage) msg.obj; 498 // There are two types of commands: 499 // 1. Interactive - user's response is required. 500 // 2. Informative - display a message, no interaction with the user. 501 // 502 // Informative commands can be handled immediately without any delay. 503 // Interactive commands can't override each other. So if a command 504 // is already in progress, we need to queue the next command until 505 // the user has responded or a timeout expired. 506 if (!isCmdInteractive(cmdMsg)) { 507 handleCmd(cmdMsg, slotId); 508 } else { 509 if (!mStkContext[slotId].mCmdInProgress) { 510 mStkContext[slotId].mCmdInProgress = true; 511 handleCmd((CatCmdMessage) msg.obj, slotId); 512 } else { 513 CatLog.d(LOG_TAG, "[Interactive][in progress]"); 514 mStkContext[slotId].mCmdsQ.addLast(new DelayedCmd(OP_CMD, 515 (CatCmdMessage) msg.obj, slotId)); 516 } 517 } 518 break; 519 case OP_RESPONSE: 520 handleCmdResponse((Bundle) msg.obj, slotId); 521 // call delayed commands if needed. 522 if (mStkContext[slotId].mCmdsQ.size() != 0) { 523 callDelayedMsg(slotId); 524 } else { 525 mStkContext[slotId].mCmdInProgress = false; 526 } 527 break; 528 case OP_END_SESSION: 529 if (!mStkContext[slotId].mCmdInProgress) { 530 mStkContext[slotId].mCmdInProgress = true; 531 handleSessionEnd(slotId); 532 } else { 533 mStkContext[slotId].mCmdsQ.addLast( 534 new DelayedCmd(OP_END_SESSION, null, slotId)); 535 } 536 break; 537 case OP_BOOT_COMPLETED: 538 CatLog.d(LOG_TAG, " OP_BOOT_COMPLETED"); 539 int i = 0; 540 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) { 541 if (mStkContext[i].mMainCmd != null) { 542 break; 543 } 544 } 545 if (i == mSimCount) { 546 StkAppInstaller.unInstall(mContext); 547 } 548 break; 549 case OP_DELAYED_MSG: 550 handleDelayedCmd(slotId); 551 break; 552 case OP_CARD_STATUS_CHANGED: 553 CatLog.d(LOG_TAG, "Card/Icc Status change received"); 554 handleCardStatusChangeAndIccRefresh((Bundle) msg.obj, slotId); 555 break; 556 case OP_SET_ACT_INST: 557 Activity act = new Activity(); 558 act = (Activity) msg.obj; 559 CatLog.d(LOG_TAG, "Set activity instance. " + act); 560 mStkContext[slotId].mActivityInstance = act; 561 break; 562 case OP_SET_DAL_INST: 563 Activity dal = new Activity(); 564 CatLog.d(LOG_TAG, "Set dialog instance. " + dal); 565 dal = (Activity) msg.obj; 566 mStkContext[slotId].mDialogInstance = dal; 567 break; 568 case OP_SET_MAINACT_INST: 569 Activity mainAct = new Activity(); 570 mainAct = (Activity) msg.obj; 571 CatLog.d(LOG_TAG, "Set activity instance. " + mainAct); 572 mStkContext[slotId].mMainActivityInstance = mainAct; 573 break; 574 case OP_LOCALE_CHANGED: 575 CatLog.d(this, "Locale Changed"); 576 for (int slot = PhoneConstants.SIM_ID_1; slot < mSimCount; slot++) { 577 checkForSetupEvent(LANGUAGE_SELECTION_EVENT, (Bundle) msg.obj, slot); 578 } 579 // rename all registered notification channels on locale change 580 createAllChannels(); 581 break; 582 case OP_ALPHA_NOTIFY: 583 handleAlphaNotify((Bundle) msg.obj); 584 break; 585 case OP_IDLE_SCREEN: 586 for (int slot = 0; slot < mSimCount; slot++) { 587 if (mStkContext[slot] != null) { 588 handleIdleScreen(slot); 589 } 590 } 591 break; 592 } 593 } 594 handleCardStatusChangeAndIccRefresh(Bundle args, int slotId)595 private void handleCardStatusChangeAndIccRefresh(Bundle args, int slotId) { 596 boolean cardStatus = args.getBoolean(AppInterface.CARD_STATUS); 597 598 CatLog.d(LOG_TAG, "CardStatus: " + cardStatus); 599 if (cardStatus == false) { 600 CatLog.d(LOG_TAG, "CARD is ABSENT"); 601 // Uninstall STKAPP, Clear Idle text, Stop StkAppService 602 mNotificationManager.cancel(getNotificationId(slotId)); 603 if (isAllOtherCardsAbsent(slotId)) { 604 CatLog.d(LOG_TAG, "All CARDs are ABSENT"); 605 StkAppInstaller.unInstall(mContext); 606 stopSelf(); 607 } 608 } else { 609 IccRefreshResponse state = new IccRefreshResponse(); 610 state.refreshResult = args.getInt(AppInterface.REFRESH_RESULT); 611 612 CatLog.d(LOG_TAG, "Icc Refresh Result: "+ state.refreshResult); 613 if ((state.refreshResult == IccRefreshResponse.REFRESH_RESULT_INIT) || 614 (state.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET)) { 615 // Clear Idle Text 616 mNotificationManager.cancel(getNotificationId(slotId)); 617 } 618 619 if (state.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET) { 620 // Uninstall STkmenu 621 if (isAllOtherCardsAbsent(slotId)) { 622 StkAppInstaller.unInstall(mContext); 623 } 624 mStkContext[slotId].mCurrentMenu = null; 625 mStkContext[slotId].mMainCmd = null; 626 } 627 } 628 } 629 } 630 /* 631 * Check if all SIMs are absent except the id of slot equals "slotId". 632 */ isAllOtherCardsAbsent(int slotId)633 private boolean isAllOtherCardsAbsent(int slotId) { 634 TelephonyManager mTm = (TelephonyManager) mContext.getSystemService( 635 Context.TELEPHONY_SERVICE); 636 int i = 0; 637 638 for (i = 0; i < mSimCount; i++) { 639 if (i != slotId && mTm.hasIccCard(i)) { 640 break; 641 } 642 } 643 if (i == mSimCount) { 644 return true; 645 } else { 646 return false; 647 } 648 } 649 650 /* 651 * If the device is not in an interactive state, we can assume 652 * that the screen is idle. 653 */ isScreenIdle()654 private boolean isScreenIdle() { 655 return (!mPowerManager.isInteractive()); 656 } 657 handleIdleScreen(int slotId)658 private void handleIdleScreen(int slotId) { 659 660 // If the idle screen event is present in the list need to send the 661 // response to SIM. 662 CatLog.d(this, "Need to send IDLE SCREEN Available event to SIM"); 663 checkForSetupEvent(IDLE_SCREEN_AVAILABLE_EVENT, null, slotId); 664 665 if (mStkContext[slotId].mIdleModeTextCmd != null) { 666 launchIdleText(slotId); 667 } 668 } 669 sendScreenBusyResponse(int slotId)670 private void sendScreenBusyResponse(int slotId) { 671 if (mStkContext[slotId].mCurrentCmd == null) { 672 return; 673 } 674 CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentCmd); 675 CatLog.d(this, "SCREEN_BUSY"); 676 resMsg.setResultCode(ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS); 677 mStkService[slotId].onCmdResponse(resMsg); 678 if (mStkContext[slotId].mCmdsQ.size() != 0) { 679 callDelayedMsg(slotId); 680 } else { 681 mStkContext[slotId].mCmdInProgress = false; 682 } 683 } 684 sendResponse(int resId, int slotId, boolean confirm)685 private void sendResponse(int resId, int slotId, boolean confirm) { 686 Message msg = mServiceHandler.obtainMessage(); 687 msg.arg1 = OP_RESPONSE; 688 msg.arg2 = slotId; 689 Bundle args = new Bundle(); 690 args.putInt(StkAppService.RES_ID, resId); 691 args.putBoolean(StkAppService.CONFIRMATION, confirm); 692 msg.obj = args; 693 mServiceHandler.sendMessage(msg); 694 } 695 isCmdInteractive(CatCmdMessage cmd)696 private boolean isCmdInteractive(CatCmdMessage cmd) { 697 switch (cmd.getCmdType()) { 698 case SEND_DTMF: 699 case SEND_SMS: 700 case SEND_SS: 701 case SEND_USSD: 702 case SET_UP_IDLE_MODE_TEXT: 703 case SET_UP_MENU: 704 case CLOSE_CHANNEL: 705 case RECEIVE_DATA: 706 case SEND_DATA: 707 case SET_UP_EVENT_LIST: 708 return false; 709 } 710 711 return true; 712 } 713 handleDelayedCmd(int slotId)714 private void handleDelayedCmd(int slotId) { 715 CatLog.d(LOG_TAG, "handleDelayedCmd, slotId: " + slotId); 716 if (mStkContext[slotId].mCmdsQ.size() != 0) { 717 DelayedCmd cmd = mStkContext[slotId].mCmdsQ.poll(); 718 if (cmd != null) { 719 CatLog.d(LOG_TAG, "handleDelayedCmd - queue size: " + 720 mStkContext[slotId].mCmdsQ.size() + 721 " id: " + cmd.id + "sim id: " + cmd.slotId); 722 switch (cmd.id) { 723 case OP_CMD: 724 handleCmd(cmd.msg, cmd.slotId); 725 break; 726 case OP_END_SESSION: 727 handleSessionEnd(cmd.slotId); 728 break; 729 } 730 } 731 } 732 } 733 callDelayedMsg(int slotId)734 private void callDelayedMsg(int slotId) { 735 Message msg = mServiceHandler.obtainMessage(); 736 msg.arg1 = OP_DELAYED_MSG; 737 msg.arg2 = slotId; 738 mServiceHandler.sendMessage(msg); 739 } 740 callSetActivityInstMsg(int inst_type, int slotId, Object obj)741 private void callSetActivityInstMsg(int inst_type, int slotId, Object obj) { 742 Message msg = mServiceHandler.obtainMessage(); 743 msg.obj = obj; 744 msg.arg1 = inst_type; 745 msg.arg2 = slotId; 746 mServiceHandler.sendMessage(msg); 747 } 748 handleSessionEnd(int slotId)749 private void handleSessionEnd(int slotId) { 750 // We should finish all pending activity if receiving END SESSION command. 751 cleanUpInstanceStackBySlot(slotId); 752 753 mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd; 754 CatLog.d(LOG_TAG, "[handleSessionEnd] - mCurrentCmd changed to mMainCmd!."); 755 mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mMainCmd; 756 CatLog.d(LOG_TAG, "slotId: " + slotId + ", mMenuState: " + 757 mStkContext[slotId].mMenuState); 758 759 mStkContext[slotId].mIsInputPending = false; 760 mStkContext[slotId].mIsMenuPending = false; 761 mStkContext[slotId].mIsDialogPending = false; 762 763 if (mStkContext[slotId].mMainCmd == null) { 764 CatLog.d(LOG_TAG, "[handleSessionEnd][mMainCmd is null!]"); 765 } 766 mStkContext[slotId].lastSelectedItem = null; 767 // In case of SET UP MENU command which removed the app, don't 768 // update the current menu member. 769 if (mStkContext[slotId].mCurrentMenu != null && mStkContext[slotId].mMainCmd != null) { 770 mStkContext[slotId].mCurrentMenu = mStkContext[slotId].mMainCmd.getMenu(); 771 } 772 CatLog.d(LOG_TAG, "[handleSessionEnd][mMenuState]" + mStkContext[slotId].mMenuIsVisible); 773 // In mutiple instance architecture, the main menu for slotId will be finished when user 774 // goes to the Stk menu of the other SIM. So, we should launch a new instance for the 775 // main menu if the main menu instance has been finished. 776 // If the current menu is secondary menu, we should launch main menu. 777 if (StkMenuActivity.STATE_SECONDARY == mStkContext[slotId].mMenuState) { 778 launchMenuActivity(null, slotId); 779 } 780 if (mStkContext[slotId].mCmdsQ.size() != 0) { 781 callDelayedMsg(slotId); 782 } else { 783 mStkContext[slotId].mCmdInProgress = false; 784 } 785 // In case a launch browser command was just confirmed, launch that url. 786 if (mStkContext[slotId].launchBrowser) { 787 mStkContext[slotId].launchBrowser = false; 788 launchBrowser(mStkContext[slotId].mBrowserSettings); 789 } 790 } 791 792 // returns true if any Stk related activity already has focus on the screen isTopOfStack()793 private boolean isTopOfStack() { 794 ActivityManager mActivityManager = (ActivityManager) mContext 795 .getSystemService(ACTIVITY_SERVICE); 796 String currentPackageName = null; 797 List<RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1); 798 if (tasks == null || tasks.get(0).topActivity == null) { 799 return false; 800 } 801 currentPackageName = tasks.get(0).topActivity.getPackageName(); 802 if (null != currentPackageName) { 803 return currentPackageName.equals(PACKAGE_NAME); 804 } 805 return false; 806 } 807 808 /** 809 * Get the boolean config from carrier config manager. 810 * 811 * @param context the context to get carrier service 812 * @param key config key defined in CarrierConfigManager 813 * @return boolean value of corresponding key. 814 */ getBooleanCarrierConfig(Context context, String key)815 private static boolean getBooleanCarrierConfig(Context context, String key) { 816 CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService( 817 Context.CARRIER_CONFIG_SERVICE); 818 PersistableBundle b = null; 819 if (configManager != null) { 820 b = configManager.getConfig(); 821 } 822 if (b != null) { 823 return b.getBoolean(key); 824 } else { 825 // Return static default defined in CarrierConfigManager. 826 return CarrierConfigManager.getDefaultConfig().getBoolean(key); 827 } 828 } 829 handleCmd(CatCmdMessage cmdMsg, int slotId)830 private void handleCmd(CatCmdMessage cmdMsg, int slotId) { 831 832 if (cmdMsg == null) { 833 return; 834 } 835 // save local reference for state tracking. 836 mStkContext[slotId].mCurrentCmd = cmdMsg; 837 boolean waitForUsersResponse = true; 838 839 mStkContext[slotId].mIsInputPending = false; 840 mStkContext[slotId].mIsMenuPending = false; 841 mStkContext[slotId].mIsDialogPending = false; 842 843 CatLog.d(LOG_TAG,"[handleCmd]" + cmdMsg.getCmdType().name()); 844 switch (cmdMsg.getCmdType()) { 845 case DISPLAY_TEXT: 846 TextMessage msg = cmdMsg.geTextMessage(); 847 waitForUsersResponse = msg.responseNeeded; 848 if (mStkContext[slotId].lastSelectedItem != null) { 849 msg.title = mStkContext[slotId].lastSelectedItem; 850 } else if (mStkContext[slotId].mMainCmd != null){ 851 msg.title = mStkContext[slotId].mMainCmd.getMenu().title; 852 } else { 853 // TODO: get the carrier name from the SIM 854 msg.title = ""; 855 } 856 //If we receive a low priority Display Text and the device is 857 // not displaying any STK related activity and the screen is not idle 858 // ( that is, device is in an interactive state), then send a screen busy 859 // terminal response. Otherwise display the message. The existing 860 // displayed message shall be updated with the new display text 861 // proactive command (Refer to ETSI TS 102 384 section 27.22.4.1.4.4.2). 862 if (!(msg.isHighPriority || mStkContext[slotId].mMenuIsVisible 863 || mStkContext[slotId].mDisplayTextDlgIsVisibile || isTopOfStack())) { 864 if(!isScreenIdle()) { 865 CatLog.d(LOG_TAG, "Screen is not idle"); 866 sendScreenBusyResponse(slotId); 867 } else { 868 launchTextDialog(slotId); 869 } 870 } else { 871 launchTextDialog(slotId); 872 } 873 break; 874 case SELECT_ITEM: 875 CatLog.d(LOG_TAG, "SELECT_ITEM +"); 876 mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mCurrentCmd; 877 mStkContext[slotId].mCurrentMenu = cmdMsg.getMenu(); 878 launchMenuActivity(cmdMsg.getMenu(), slotId); 879 break; 880 case SET_UP_MENU: 881 mStkContext[slotId].mCmdInProgress = false; 882 mStkContext[slotId].mMainCmd = mStkContext[slotId].mCurrentCmd; 883 mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mCurrentCmd; 884 mStkContext[slotId].mCurrentMenu = cmdMsg.getMenu(); 885 CatLog.d(LOG_TAG, "SET_UP_MENU [" + removeMenu(slotId) + "]"); 886 887 if (removeMenu(slotId)) { 888 int i = 0; 889 CatLog.d(LOG_TAG, "removeMenu() - Uninstall App"); 890 mStkContext[slotId].mCurrentMenu = null; 891 mStkContext[slotId].mMainCmd = null; 892 //Check other setup menu state. If all setup menu are removed, uninstall apk. 893 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) { 894 if (i != slotId 895 && (mStkContext[slotId].mSetupMenuState == STATE_UNKNOWN 896 || mStkContext[slotId].mSetupMenuState == STATE_EXIST)) { 897 CatLog.d(LOG_TAG, "Not Uninstall App:" + i + "," 898 + mStkContext[slotId].mSetupMenuState); 899 break; 900 } 901 } 902 if (i == mSimCount) { 903 StkAppInstaller.unInstall(mContext); 904 } 905 } else { 906 CatLog.d(LOG_TAG, "install App"); 907 StkAppInstaller.install(mContext); 908 } 909 if (mStkContext[slotId].mMenuIsVisible) { 910 launchMenuActivity(null, slotId); 911 } 912 break; 913 case GET_INPUT: 914 case GET_INKEY: 915 launchInputActivity(slotId); 916 break; 917 case SET_UP_IDLE_MODE_TEXT: 918 waitForUsersResponse = false; 919 mStkContext[slotId].mIdleModeTextCmd = mStkContext[slotId].mCurrentCmd; 920 TextMessage idleModeText = mStkContext[slotId].mCurrentCmd.geTextMessage(); 921 if (idleModeText == null) { 922 launchIdleText(slotId); 923 mStkContext[slotId].mIdleModeTextCmd = null; 924 } 925 mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd; 926 if ((mStkContext[slotId].mIdleModeTextCmd != null) && isScreenIdle()) { 927 CatLog.d(this, "set up idle mode"); 928 launchIdleText(slotId); 929 } 930 break; 931 case SEND_DTMF: 932 case SEND_SMS: 933 case SEND_SS: 934 case SEND_USSD: 935 case GET_CHANNEL_STATUS: 936 waitForUsersResponse = false; 937 launchEventMessage(slotId); 938 break; 939 case LAUNCH_BROWSER: 940 // The device setup process should not be interrupted by launching browser. 941 if (Settings.Global.getInt(mContext.getContentResolver(), 942 Settings.Global.DEVICE_PROVISIONED, 0) == 0) { 943 CatLog.d(this, "The command is not performed if the setup has not been completed."); 944 sendScreenBusyResponse(slotId); 945 break; 946 } 947 948 /* Check if Carrier would not want to launch browser */ 949 if (getBooleanCarrierConfig(mContext, 950 CarrierConfigManager.KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL)) { 951 CatLog.d(this, "Browser is not launched as per carrier."); 952 sendResponse(RES_ID_DONE, slotId, true); 953 break; 954 } 955 956 TextMessage alphaId = mStkContext[slotId].mCurrentCmd.geTextMessage(); 957 if ((mStkContext[slotId].mCurrentCmd.getBrowserSettings().mode 958 == LaunchBrowserMode.LAUNCH_IF_NOT_ALREADY_LAUNCHED) && 959 ((alphaId == null) || TextUtils.isEmpty(alphaId.text))) { 960 // don't need user confirmation in this case 961 // just launch the browser or spawn a new tab 962 CatLog.d(this, "Browser mode is: launch if not already launched " + 963 "and user confirmation is not currently needed.\n" + 964 "supressing confirmation dialogue and confirming silently..."); 965 mStkContext[slotId].launchBrowser = true; 966 mStkContext[slotId].mBrowserSettings = 967 mStkContext[slotId].mCurrentCmd.getBrowserSettings(); 968 sendResponse(RES_ID_CONFIRM, slotId, true); 969 } else { 970 launchConfirmationDialog(alphaId, slotId); 971 } 972 break; 973 case SET_UP_CALL: 974 TextMessage mesg = mStkContext[slotId].mCurrentCmd.getCallSettings().confirmMsg; 975 if((mesg != null) && (mesg.text == null || mesg.text.length() == 0)) { 976 mesg.text = getResources().getString(R.string.default_setup_call_msg); 977 } 978 CatLog.d(this, "SET_UP_CALL mesg.text " + mesg.text); 979 launchConfirmationDialog(mesg, slotId); 980 break; 981 case PLAY_TONE: 982 launchToneDialog(slotId); 983 break; 984 case OPEN_CHANNEL: 985 launchOpenChannelDialog(slotId); 986 break; 987 case CLOSE_CHANNEL: 988 case RECEIVE_DATA: 989 case SEND_DATA: 990 TextMessage m = mStkContext[slotId].mCurrentCmd.geTextMessage(); 991 992 if ((m != null) && (m.text == null)) { 993 switch(cmdMsg.getCmdType()) { 994 case CLOSE_CHANNEL: 995 m.text = getResources().getString(R.string.default_close_channel_msg); 996 break; 997 case RECEIVE_DATA: 998 m.text = getResources().getString(R.string.default_receive_data_msg); 999 break; 1000 case SEND_DATA: 1001 m.text = getResources().getString(R.string.default_send_data_msg); 1002 break; 1003 } 1004 } 1005 /* 1006 * Display indication in the form of a toast to the user if required. 1007 */ 1008 launchEventMessage(slotId); 1009 break; 1010 case SET_UP_EVENT_LIST: 1011 mStkContext[slotId].mSetupEventListSettings = 1012 mStkContext[slotId].mCurrentCmd.getSetEventList(); 1013 mStkContext[slotId].mCurrentSetupEventCmd = mStkContext[slotId].mCurrentCmd; 1014 mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd; 1015 if (isScreenIdle()) { 1016 CatLog.d(this," Check if IDLE_SCREEN_AVAILABLE_EVENT is present in List"); 1017 checkForSetupEvent(IDLE_SCREEN_AVAILABLE_EVENT, null, slotId); 1018 } 1019 break; 1020 } 1021 1022 if (!waitForUsersResponse) { 1023 if (mStkContext[slotId].mCmdsQ.size() != 0) { 1024 callDelayedMsg(slotId); 1025 } else { 1026 mStkContext[slotId].mCmdInProgress = false; 1027 } 1028 } 1029 } 1030 handleCmdResponse(Bundle args, int slotId)1031 private void handleCmdResponse(Bundle args, int slotId) { 1032 CatLog.d(LOG_TAG, "handleCmdResponse, sim id: " + slotId); 1033 if (mStkContext[slotId].mCurrentCmd == null) { 1034 return; 1035 } 1036 1037 if (mStkService[slotId] == null) { 1038 mStkService[slotId] = CatService.getInstance(slotId); 1039 if (mStkService[slotId] == null) { 1040 // This should never happen (we should be responding only to a message 1041 // that arrived from StkService). It has to exist by this time 1042 CatLog.d(LOG_TAG, "Exception! mStkService is null when we need to send response."); 1043 throw new RuntimeException("mStkService is null when we need to send response"); 1044 } 1045 } 1046 1047 CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentCmd); 1048 1049 // set result code 1050 boolean helpRequired = args.getBoolean(HELP, false); 1051 boolean confirmed = false; 1052 1053 switch(args.getInt(RES_ID)) { 1054 case RES_ID_MENU_SELECTION: 1055 CatLog.d(LOG_TAG, "MENU_SELECTION=" + mStkContext[slotId]. 1056 mCurrentMenuCmd.getCmdType()); 1057 int menuSelection = args.getInt(MENU_SELECTION); 1058 switch(mStkContext[slotId].mCurrentMenuCmd.getCmdType()) { 1059 case SET_UP_MENU: 1060 case SELECT_ITEM: 1061 mStkContext[slotId].lastSelectedItem = getItemName(menuSelection, slotId); 1062 if (helpRequired) { 1063 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED); 1064 } else { 1065 resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ? 1066 ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK); 1067 } 1068 resMsg.setMenuSelection(menuSelection); 1069 break; 1070 } 1071 break; 1072 case RES_ID_INPUT: 1073 CatLog.d(LOG_TAG, "RES_ID_INPUT"); 1074 String input = args.getString(INPUT); 1075 if (input != null && (null != mStkContext[slotId].mCurrentCmd.geInput()) && 1076 (mStkContext[slotId].mCurrentCmd.geInput().yesNo)) { 1077 boolean yesNoSelection = input 1078 .equals(StkInputActivity.YES_STR_RESPONSE); 1079 resMsg.setYesNo(yesNoSelection); 1080 } else { 1081 if (helpRequired) { 1082 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED); 1083 } else { 1084 resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ? 1085 ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK); 1086 resMsg.setInput(input); 1087 } 1088 } 1089 break; 1090 case RES_ID_CONFIRM: 1091 CatLog.d(this, "RES_ID_CONFIRM"); 1092 confirmed = args.getBoolean(CONFIRMATION); 1093 switch (mStkContext[slotId].mCurrentCmd.getCmdType()) { 1094 case DISPLAY_TEXT: 1095 if (confirmed) { 1096 resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ? 1097 ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK); 1098 } else { 1099 resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER); 1100 } 1101 break; 1102 case LAUNCH_BROWSER: 1103 resMsg.setResultCode(confirmed ? ResultCode.OK 1104 : ResultCode.UICC_SESSION_TERM_BY_USER); 1105 if (confirmed) { 1106 mStkContext[slotId].launchBrowser = true; 1107 mStkContext[slotId].mBrowserSettings = 1108 mStkContext[slotId].mCurrentCmd.getBrowserSettings(); 1109 } 1110 break; 1111 case SET_UP_CALL: 1112 resMsg.setResultCode(ResultCode.OK); 1113 resMsg.setConfirmation(confirmed); 1114 if (confirmed) { 1115 launchEventMessage(slotId, 1116 mStkContext[slotId].mCurrentCmd.getCallSettings().callMsg); 1117 } 1118 break; 1119 } 1120 break; 1121 case RES_ID_DONE: 1122 resMsg.setResultCode(ResultCode.OK); 1123 break; 1124 case RES_ID_BACKWARD: 1125 CatLog.d(LOG_TAG, "RES_ID_BACKWARD"); 1126 resMsg.setResultCode(ResultCode.BACKWARD_MOVE_BY_USER); 1127 break; 1128 case RES_ID_END_SESSION: 1129 CatLog.d(LOG_TAG, "RES_ID_END_SESSION"); 1130 resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER); 1131 break; 1132 case RES_ID_TIMEOUT: 1133 CatLog.d(LOG_TAG, "RES_ID_TIMEOUT"); 1134 // GCF test-case 27.22.4.1.1 Expected Sequence 1.5 (DISPLAY TEXT, 1135 // Clear message after delay, successful) expects result code OK. 1136 // If the command qualifier specifies no user response is required 1137 // then send OK instead of NO_RESPONSE_FROM_USER 1138 if ((mStkContext[slotId].mCurrentCmd.getCmdType().value() == 1139 AppInterface.CommandType.DISPLAY_TEXT.value()) 1140 && (mStkContext[slotId].mCurrentCmd.geTextMessage().userClear == false)) { 1141 resMsg.setResultCode(ResultCode.OK); 1142 } else { 1143 resMsg.setResultCode(ResultCode.NO_RESPONSE_FROM_USER); 1144 } 1145 break; 1146 case RES_ID_CHOICE: 1147 int choice = args.getInt(CHOICE); 1148 CatLog.d(this, "User Choice=" + choice); 1149 switch (choice) { 1150 case YES: 1151 resMsg.setResultCode(ResultCode.OK); 1152 confirmed = true; 1153 break; 1154 case NO: 1155 resMsg.setResultCode(ResultCode.USER_NOT_ACCEPT); 1156 break; 1157 } 1158 1159 if (mStkContext[slotId].mCurrentCmd.getCmdType().value() == 1160 AppInterface.CommandType.OPEN_CHANNEL.value()) { 1161 resMsg.setConfirmation(confirmed); 1162 } 1163 break; 1164 1165 default: 1166 CatLog.d(LOG_TAG, "Unknown result id"); 1167 return; 1168 } 1169 1170 if (null != mStkContext[slotId].mCurrentCmd && 1171 null != mStkContext[slotId].mCurrentCmd.getCmdType()) { 1172 CatLog.d(LOG_TAG, "handleCmdResponse- cmdName[" + 1173 mStkContext[slotId].mCurrentCmd.getCmdType().name() + "]"); 1174 } 1175 mStkService[slotId].onCmdResponse(resMsg); 1176 } 1177 1178 /** 1179 * Returns 0 or FLAG_ACTIVITY_NO_USER_ACTION, 0 means the user initiated the action. 1180 * 1181 * @param userAction If the userAction is yes then we always return 0 otherwise 1182 * mMenuIsVisible is used to determine what to return. If mMenuIsVisible is true 1183 * then we are the foreground app and we'll return 0 as from our perspective a 1184 * user action did cause. If it's false than we aren't the foreground app and 1185 * FLAG_ACTIVITY_NO_USER_ACTION is returned. 1186 * 1187 * @return 0 or FLAG_ACTIVITY_NO_USER_ACTION 1188 */ getFlagActivityNoUserAction(InitiatedByUserAction userAction, int slotId)1189 private int getFlagActivityNoUserAction(InitiatedByUserAction userAction, int slotId) { 1190 return ((userAction == InitiatedByUserAction.yes) | mStkContext[slotId].mMenuIsVisible) 1191 ? 0 : Intent.FLAG_ACTIVITY_NO_USER_ACTION; 1192 } 1193 /** 1194 * This method is used for cleaning up pending instances in stack. 1195 */ cleanUpInstanceStackBySlot(int slotId)1196 private void cleanUpInstanceStackBySlot(int slotId) { 1197 Activity activity = mStkContext[slotId].getPendingActivityInstance(); 1198 Activity dialog = mStkContext[slotId].getPendingDialogInstance(); 1199 CatLog.d(LOG_TAG, "cleanUpInstanceStackBySlot slotId: " + slotId); 1200 if (mStkContext[slotId].mCurrentCmd == null) { 1201 CatLog.d(LOG_TAG, "current cmd is null."); 1202 return; 1203 } 1204 if (activity != null) { 1205 CatLog.d(LOG_TAG, "current cmd type: " + 1206 mStkContext[slotId].mCurrentCmd.getCmdType()); 1207 if (mStkContext[slotId].mCurrentCmd.getCmdType().value() == 1208 AppInterface.CommandType.GET_INPUT.value() || 1209 mStkContext[slotId].mCurrentCmd.getCmdType().value() == 1210 AppInterface.CommandType.GET_INKEY.value()) { 1211 mStkContext[slotId].mIsInputPending = true; 1212 } else if (mStkContext[slotId].mCurrentCmd.getCmdType().value() == 1213 AppInterface.CommandType.SET_UP_MENU.value() || 1214 mStkContext[slotId].mCurrentCmd.getCmdType().value() == 1215 AppInterface.CommandType.SELECT_ITEM.value()) { 1216 mStkContext[slotId].mIsMenuPending = true; 1217 } else { 1218 } 1219 CatLog.d(LOG_TAG, "finish pending activity."); 1220 activity.finish(); 1221 mStkContext[slotId].mActivityInstance = null; 1222 } 1223 if (dialog != null) { 1224 CatLog.d(LOG_TAG, "finish pending dialog."); 1225 mStkContext[slotId].mIsDialogPending = true; 1226 dialog.finish(); 1227 mStkContext[slotId].mDialogInstance = null; 1228 } 1229 } 1230 /** 1231 * This method is used for restoring pending instances from stack. 1232 */ restoreInstanceFromStackBySlot(int slotId)1233 private void restoreInstanceFromStackBySlot(int slotId) { 1234 AppInterface.CommandType cmdType = mStkContext[slotId].mCurrentCmd.getCmdType(); 1235 1236 CatLog.d(LOG_TAG, "restoreInstanceFromStackBySlot cmdType : " + cmdType); 1237 switch(cmdType) { 1238 case GET_INPUT: 1239 case GET_INKEY: 1240 launchInputActivity(slotId); 1241 //Set mMenuIsVisible to true for showing main menu for 1242 //following session end command. 1243 mStkContext[slotId].mMenuIsVisible = true; 1244 break; 1245 case DISPLAY_TEXT: 1246 launchTextDialog(slotId); 1247 break; 1248 case LAUNCH_BROWSER: 1249 launchConfirmationDialog(mStkContext[slotId].mCurrentCmd.geTextMessage(), 1250 slotId); 1251 break; 1252 case OPEN_CHANNEL: 1253 launchOpenChannelDialog(slotId); 1254 break; 1255 case SET_UP_CALL: 1256 launchConfirmationDialog(mStkContext[slotId].mCurrentCmd.getCallSettings(). 1257 confirmMsg, slotId); 1258 break; 1259 case SET_UP_MENU: 1260 case SELECT_ITEM: 1261 launchMenuActivity(null, slotId); 1262 break; 1263 default: 1264 break; 1265 } 1266 } 1267 launchMenuActivity(Menu menu, int slotId)1268 private void launchMenuActivity(Menu menu, int slotId) { 1269 Intent newIntent = new Intent(Intent.ACTION_VIEW); 1270 String targetActivity = STK_MENU_ACTIVITY_NAME; 1271 String uriString = STK_MENU_URI + System.currentTimeMillis(); 1272 //Set unique URI to create a new instance of activity for different slotId. 1273 Uri uriData = Uri.parse(uriString); 1274 1275 CatLog.d(LOG_TAG, "launchMenuActivity, slotId: " + slotId + " , " + 1276 uriData.toString() + " , " + mStkContext[slotId].mOpCode + ", " 1277 + mStkContext[slotId].mMenuState); 1278 newIntent.setClassName(PACKAGE_NAME, targetActivity); 1279 int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK; 1280 1281 if (menu == null) { 1282 // We assume this was initiated by the user pressing the tool kit icon 1283 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.yes, slotId); 1284 if (mStkContext[slotId].mOpCode == OP_END_SESSION) { 1285 CatLog.d(LOG_TAG, "launchMenuActivity, return OP_END_SESSION"); 1286 mStkContext[slotId].mMenuState = StkMenuActivity.STATE_MAIN; 1287 if (mStkContext[slotId].mMainActivityInstance != null) { 1288 CatLog.d(LOG_TAG, "launchMenuActivity, mMainActivityInstance is not null"); 1289 return; 1290 } 1291 } 1292 1293 //If the last pending menu is secondary menu, "STATE" should be "STATE_SECONDARY". 1294 //Otherwise, it should be "STATE_MAIN". 1295 if (mStkContext[slotId].mOpCode == OP_LAUNCH_APP && 1296 mStkContext[slotId].mMenuState == StkMenuActivity.STATE_SECONDARY) { 1297 newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY); 1298 } else { 1299 newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN); 1300 mStkContext[slotId].mMenuState = StkMenuActivity.STATE_MAIN; 1301 } 1302 } else { 1303 // We don't know and we'll let getFlagActivityNoUserAction decide. 1304 intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId); 1305 newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY); 1306 mStkContext[slotId].mMenuState = StkMenuActivity.STATE_SECONDARY; 1307 } 1308 newIntent.putExtra(SLOT_ID, slotId); 1309 newIntent.setData(uriData); 1310 newIntent.setFlags(intentFlags); 1311 mContext.startActivity(newIntent); 1312 } 1313 launchInputActivity(int slotId)1314 private void launchInputActivity(int slotId) { 1315 Intent newIntent = new Intent(Intent.ACTION_VIEW); 1316 String targetActivity = STK_INPUT_ACTIVITY_NAME; 1317 String uriString = STK_INPUT_URI + System.currentTimeMillis(); 1318 //Set unique URI to create a new instance of activity for different slotId. 1319 Uri uriData = Uri.parse(uriString); 1320 1321 CatLog.d(LOG_TAG, "launchInputActivity, slotId: " + slotId); 1322 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1323 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId)); 1324 newIntent.setClassName(PACKAGE_NAME, targetActivity); 1325 newIntent.putExtra("INPUT", mStkContext[slotId].mCurrentCmd.geInput()); 1326 newIntent.putExtra(SLOT_ID, slotId); 1327 newIntent.setData(uriData); 1328 mContext.startActivity(newIntent); 1329 } 1330 launchTextDialog(int slotId)1331 private void launchTextDialog(int slotId) { 1332 CatLog.d(LOG_TAG, "launchTextDialog, slotId: " + slotId); 1333 Intent newIntent = new Intent(); 1334 String targetActivity = STK_DIALOG_ACTIVITY_NAME; 1335 int action = getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId); 1336 String uriString = STK_DIALOG_URI + System.currentTimeMillis(); 1337 //Set unique URI to create a new instance of activity for different slotId. 1338 Uri uriData = Uri.parse(uriString); 1339 if (newIntent != null) { 1340 newIntent.setClassName(PACKAGE_NAME, targetActivity); 1341 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1342 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 1343 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId)); 1344 newIntent.setData(uriData); 1345 newIntent.putExtra("TEXT", mStkContext[slotId].mCurrentCmd.geTextMessage()); 1346 newIntent.putExtra(SLOT_ID, slotId); 1347 startActivity(newIntent); 1348 // For display texts with immediate response, send the terminal response 1349 // immediately. responseNeeded will be false, if display text command has 1350 // the immediate response tlv. 1351 if (!mStkContext[slotId].mCurrentCmd.geTextMessage().responseNeeded) { 1352 sendResponse(RES_ID_CONFIRM, slotId, true); 1353 } 1354 } 1355 } 1356 isStkDialogActivated(Context context)1357 public boolean isStkDialogActivated(Context context) { 1358 String stkDialogActivity = "com.android.stk.StkDialogActivity"; 1359 boolean activated = false; 1360 final ActivityManager am = (ActivityManager) context.getSystemService( 1361 Context.ACTIVITY_SERVICE); 1362 String topActivity = am.getRunningTasks(1).get(0).topActivity.getClassName(); 1363 1364 CatLog.d(LOG_TAG, "isStkDialogActivated: " + topActivity); 1365 if (topActivity.equals(stkDialogActivity)) { 1366 activated = true; 1367 } 1368 CatLog.d(LOG_TAG, "activated : " + activated); 1369 return activated; 1370 } 1371 sendSetUpEventResponse(int event, byte[] addedInfo, int slotId)1372 private void sendSetUpEventResponse(int event, byte[] addedInfo, int slotId) { 1373 CatLog.d(this, "sendSetUpEventResponse: event : " + event + "slotId = " + slotId); 1374 1375 if (mStkContext[slotId].mCurrentSetupEventCmd == null){ 1376 CatLog.e(this, "mCurrentSetupEventCmd is null"); 1377 return; 1378 } 1379 1380 CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentSetupEventCmd); 1381 1382 resMsg.setResultCode(ResultCode.OK); 1383 resMsg.setEventDownload(event, addedInfo); 1384 1385 mStkService[slotId].onCmdResponse(resMsg); 1386 } 1387 checkForSetupEvent(int event, Bundle args, int slotId)1388 private void checkForSetupEvent(int event, Bundle args, int slotId) { 1389 boolean eventPresent = false; 1390 byte[] addedInfo = null; 1391 CatLog.d(this, "Event :" + event); 1392 1393 if (mStkContext[slotId].mSetupEventListSettings != null) { 1394 /* Checks if the event is present in the EventList updated by last 1395 * SetupEventList Proactive Command */ 1396 for (int i : mStkContext[slotId].mSetupEventListSettings.eventList) { 1397 if (event == i) { 1398 eventPresent = true; 1399 break; 1400 } 1401 } 1402 1403 /* If Event is present send the response to ICC */ 1404 if (eventPresent == true) { 1405 CatLog.d(this, " Event " + event + "exists in the EventList"); 1406 1407 switch (event) { 1408 case IDLE_SCREEN_AVAILABLE_EVENT: 1409 sendSetUpEventResponse(event, addedInfo, slotId); 1410 removeSetUpEvent(event, slotId); 1411 break; 1412 case LANGUAGE_SELECTION_EVENT: 1413 String language = mContext 1414 .getResources().getConfiguration().locale.getLanguage(); 1415 CatLog.d(this, "language: " + language); 1416 // Each language code is a pair of alpha-numeric characters. 1417 // Each alpha-numeric character shall be coded on one byte 1418 // using the SMS default 7-bit coded alphabet 1419 addedInfo = GsmAlphabet.stringToGsm8BitPacked(language); 1420 sendSetUpEventResponse(event, addedInfo, slotId); 1421 break; 1422 default: 1423 break; 1424 } 1425 } else { 1426 CatLog.e(this, " Event does not exist in the EventList"); 1427 } 1428 } else { 1429 CatLog.e(this, "SetupEventList is not received. Ignoring the event: " + event); 1430 } 1431 } 1432 removeSetUpEvent(int event, int slotId)1433 private void removeSetUpEvent(int event, int slotId) { 1434 CatLog.d(this, "Remove Event :" + event); 1435 1436 if (mStkContext[slotId].mSetupEventListSettings != null) { 1437 /* 1438 * Make new Eventlist without the event 1439 */ 1440 for (int i = 0; i < mStkContext[slotId].mSetupEventListSettings.eventList.length; i++) { 1441 if (event == mStkContext[slotId].mSetupEventListSettings.eventList[i]) { 1442 mStkContext[slotId].mSetupEventListSettings.eventList[i] = INVALID_SETUP_EVENT; 1443 break; 1444 } 1445 } 1446 } 1447 } 1448 launchEventMessage(int slotId)1449 private void launchEventMessage(int slotId) { 1450 launchEventMessage(slotId, mStkContext[slotId].mCurrentCmd.geTextMessage()); 1451 } 1452 launchEventMessage(int slotId, TextMessage msg)1453 private void launchEventMessage(int slotId, TextMessage msg) { 1454 if (msg == null || (msg.text != null && msg.text.length() == 0)) { 1455 CatLog.d(LOG_TAG, "launchEventMessage return"); 1456 return; 1457 } 1458 1459 Toast toast = new Toast(mContext.getApplicationContext()); 1460 LayoutInflater inflate = (LayoutInflater) mContext 1461 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 1462 View v = inflate.inflate(R.layout.stk_event_msg, null); 1463 TextView tv = (TextView) v 1464 .findViewById(com.android.internal.R.id.message); 1465 ImageView iv = (ImageView) v 1466 .findViewById(com.android.internal.R.id.icon); 1467 if (msg.icon != null) { 1468 iv.setImageBitmap(msg.icon); 1469 } else { 1470 iv.setVisibility(View.GONE); 1471 } 1472 /* In case of 'self explanatory' stkapp should display the specified 1473 * icon in proactive command (but not the alpha string). 1474 * If icon is non-self explanatory and if the icon could not be displayed 1475 * then alpha string or text data should be displayed 1476 * Ref: ETSI 102.223,section 6.5.4 1477 */ 1478 if (mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() || 1479 msg.icon == null || !msg.iconSelfExplanatory) { 1480 tv.setText(msg.text); 1481 } 1482 1483 toast.setView(v); 1484 toast.setDuration(Toast.LENGTH_LONG); 1485 toast.setGravity(Gravity.BOTTOM, 0, 0); 1486 toast.show(); 1487 } 1488 launchConfirmationDialog(TextMessage msg, int slotId)1489 private void launchConfirmationDialog(TextMessage msg, int slotId) { 1490 msg.title = mStkContext[slotId].lastSelectedItem; 1491 Intent newIntent = new Intent(); 1492 String targetActivity = STK_DIALOG_ACTIVITY_NAME; 1493 String uriString = STK_DIALOG_URI + System.currentTimeMillis(); 1494 //Set unique URI to create a new instance of activity for different slotId. 1495 Uri uriData = Uri.parse(uriString); 1496 1497 if (newIntent != null) { 1498 newIntent.setClassName(this, targetActivity); 1499 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1500 | Intent.FLAG_ACTIVITY_NO_HISTORY 1501 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 1502 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId)); 1503 newIntent.putExtra("TEXT", msg); 1504 newIntent.putExtra(SLOT_ID, slotId); 1505 newIntent.setData(uriData); 1506 startActivity(newIntent); 1507 } 1508 } 1509 launchBrowser(BrowserSettings settings)1510 private void launchBrowser(BrowserSettings settings) { 1511 if (settings == null) { 1512 return; 1513 } 1514 1515 Uri data = null; 1516 String url; 1517 if (settings.url == null) { 1518 // if the command did not contain a URL, 1519 // launch the browser to the default homepage. 1520 CatLog.d(this, "no url data provided by proactive command." + 1521 " launching browser with stk default URL ... "); 1522 url = SystemProperties.get(STK_BROWSER_DEFAULT_URL_SYSPROP, 1523 "http://www.google.com"); 1524 } else { 1525 CatLog.d(this, "launch browser command has attached url = " + settings.url); 1526 url = settings.url; 1527 } 1528 1529 if (url.startsWith("http://") || url.startsWith("https://")) { 1530 data = Uri.parse(url); 1531 CatLog.d(this, "launching browser with url = " + url); 1532 } else { 1533 String modifiedUrl = "http://" + url; 1534 data = Uri.parse(modifiedUrl); 1535 CatLog.d(this, "launching browser with modified url = " + modifiedUrl); 1536 } 1537 1538 Intent intent = new Intent(Intent.ACTION_VIEW); 1539 intent.setData(data); 1540 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1541 switch (settings.mode) { 1542 case USE_EXISTING_BROWSER: 1543 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 1544 break; 1545 case LAUNCH_NEW_BROWSER: 1546 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); 1547 break; 1548 case LAUNCH_IF_NOT_ALREADY_LAUNCHED: 1549 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 1550 break; 1551 } 1552 // start browser activity 1553 startActivity(intent); 1554 // a small delay, let the browser start, before processing the next command. 1555 // this is good for scenarios where a related DISPLAY TEXT command is 1556 // followed immediately. 1557 try { 1558 Thread.sleep(3000); 1559 } catch (InterruptedException e) {} 1560 } 1561 launchIdleText(int slotId)1562 private void launchIdleText(int slotId) { 1563 TextMessage msg = mStkContext[slotId].mIdleModeTextCmd.geTextMessage(); 1564 1565 if (msg == null || msg.text ==null) { 1566 CatLog.d(LOG_TAG, msg == null ? "mCurrent.getTextMessage is NULL" 1567 : "mCurrent.getTextMessage.text is NULL"); 1568 mNotificationManager.cancel(getNotificationId(slotId)); 1569 return; 1570 } else { 1571 CatLog.d(LOG_TAG, "launchIdleText - text[" + msg.text 1572 + "] iconSelfExplanatory[" + msg.iconSelfExplanatory 1573 + "] icon[" + msg.icon + "], sim id: " + slotId); 1574 CatLog.d(LOG_TAG, "Add IdleMode text"); 1575 PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, 1576 new Intent(mContext, StkAppService.class), 0); 1577 createAllChannels(); 1578 final Notification.Builder notificationBuilder = new Notification.Builder( 1579 StkAppService.this, STK_NOTIFICATION_CHANNEL_ID); 1580 if (mStkContext[slotId].mMainCmd != null && 1581 mStkContext[slotId].mMainCmd.getMenu() != null) { 1582 notificationBuilder.setContentTitle(mStkContext[slotId].mMainCmd.getMenu().title); 1583 } else { 1584 notificationBuilder.setContentTitle(""); 1585 } 1586 notificationBuilder 1587 .setSmallIcon(com.android.internal.R.drawable.stat_notify_sim_toolkit); 1588 notificationBuilder.setContentIntent(pendingIntent); 1589 notificationBuilder.setOngoing(true); 1590 // Set text and icon for the status bar and notification body. 1591 if (mStkContext[slotId].mIdleModeTextCmd.hasIconLoadFailed() || 1592 !msg.iconSelfExplanatory) { 1593 notificationBuilder.setContentText(msg.text); 1594 notificationBuilder.setTicker(msg.text); 1595 } 1596 if (msg.icon != null) { 1597 notificationBuilder.setLargeIcon(msg.icon); 1598 } else { 1599 Bitmap bitmapIcon = BitmapFactory.decodeResource(StkAppService.this 1600 .getResources().getSystem(), 1601 com.android.internal.R.drawable.stat_notify_sim_toolkit); 1602 notificationBuilder.setLargeIcon(bitmapIcon); 1603 } 1604 notificationBuilder.setColor(mContext.getResources().getColor( 1605 com.android.internal.R.color.system_notification_accent_color)); 1606 mNotificationManager.notify(getNotificationId(slotId), notificationBuilder.build()); 1607 } 1608 } 1609 1610 /** Creates the notification channel and registers it with NotificationManager. 1611 * If a channel with the same ID is already registered, NotificationManager will 1612 * ignore this call. 1613 */ createAllChannels()1614 private void createAllChannels() { 1615 mNotificationManager.createNotificationChannel(new NotificationChannel( 1616 STK_NOTIFICATION_CHANNEL_ID, 1617 getResources().getString(R.string.stk_channel_name), 1618 NotificationManager.IMPORTANCE_MIN)); 1619 } 1620 launchToneDialog(int slotId)1621 private void launchToneDialog(int slotId) { 1622 Intent newIntent = new Intent(this, ToneDialog.class); 1623 String uriString = STK_TONE_URI + slotId; 1624 Uri uriData = Uri.parse(uriString); 1625 //Set unique URI to create a new instance of activity for different slotId. 1626 CatLog.d(LOG_TAG, "launchToneDialog, slotId: " + slotId); 1627 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1628 | Intent.FLAG_ACTIVITY_NO_HISTORY 1629 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 1630 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId)); 1631 newIntent.putExtra("TEXT", mStkContext[slotId].mCurrentCmd.geTextMessage()); 1632 newIntent.putExtra("TONE", mStkContext[slotId].mCurrentCmd.getToneSettings()); 1633 newIntent.putExtra(SLOT_ID, slotId); 1634 newIntent.setData(uriData); 1635 startActivity(newIntent); 1636 } 1637 launchOpenChannelDialog(final int slotId)1638 private void launchOpenChannelDialog(final int slotId) { 1639 TextMessage msg = mStkContext[slotId].mCurrentCmd.geTextMessage(); 1640 if (msg == null) { 1641 CatLog.d(LOG_TAG, "msg is null, return here"); 1642 return; 1643 } 1644 1645 msg.title = getResources().getString(R.string.stk_dialog_title); 1646 if (msg.text == null) { 1647 msg.text = getResources().getString(R.string.default_open_channel_msg); 1648 } 1649 1650 final AlertDialog dialog = new AlertDialog.Builder(mContext) 1651 .setIconAttribute(android.R.attr.alertDialogIcon) 1652 .setTitle(msg.title) 1653 .setMessage(msg.text) 1654 .setCancelable(false) 1655 .setPositiveButton(getResources().getString(R.string.stk_dialog_accept), 1656 new DialogInterface.OnClickListener() { 1657 public void onClick(DialogInterface dialog, int which) { 1658 Bundle args = new Bundle(); 1659 args.putInt(RES_ID, RES_ID_CHOICE); 1660 args.putInt(CHOICE, YES); 1661 Message message = mServiceHandler.obtainMessage(); 1662 message.arg1 = OP_RESPONSE; 1663 message.arg2 = slotId; 1664 message.obj = args; 1665 mServiceHandler.sendMessage(message); 1666 } 1667 }) 1668 .setNegativeButton(getResources().getString(R.string.stk_dialog_reject), 1669 new DialogInterface.OnClickListener() { 1670 public void onClick(DialogInterface dialog, int which) { 1671 Bundle args = new Bundle(); 1672 args.putInt(RES_ID, RES_ID_CHOICE); 1673 args.putInt(CHOICE, NO); 1674 Message message = mServiceHandler.obtainMessage(); 1675 message.arg1 = OP_RESPONSE; 1676 message.arg2 = slotId; 1677 message.obj = args; 1678 mServiceHandler.sendMessage(message); 1679 } 1680 }) 1681 .create(); 1682 1683 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1684 if (!mContext.getResources().getBoolean( 1685 com.android.internal.R.bool.config_sf_slowBlur)) { 1686 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); 1687 } 1688 1689 dialog.show(); 1690 } 1691 launchTransientEventMessage(int slotId)1692 private void launchTransientEventMessage(int slotId) { 1693 TextMessage msg = mStkContext[slotId].mCurrentCmd.geTextMessage(); 1694 if (msg == null) { 1695 CatLog.d(LOG_TAG, "msg is null, return here"); 1696 return; 1697 } 1698 1699 msg.title = getResources().getString(R.string.stk_dialog_title); 1700 1701 final AlertDialog dialog = new AlertDialog.Builder(mContext) 1702 .setIconAttribute(android.R.attr.alertDialogIcon) 1703 .setTitle(msg.title) 1704 .setMessage(msg.text) 1705 .setCancelable(false) 1706 .setPositiveButton(getResources().getString(android.R.string.ok), 1707 new DialogInterface.OnClickListener() { 1708 public void onClick(DialogInterface dialog, int which) { 1709 } 1710 }) 1711 .create(); 1712 1713 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1714 if (!mContext.getResources().getBoolean( 1715 com.android.internal.R.bool.config_sf_slowBlur)) { 1716 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); 1717 } 1718 1719 dialog.show(); 1720 } 1721 getNotificationId(int slotId)1722 private int getNotificationId(int slotId) { 1723 int notifyId = STK_NOTIFICATION_ID; 1724 if (slotId >= 0 && slotId < mSimCount) { 1725 notifyId += slotId; 1726 } else { 1727 CatLog.d(LOG_TAG, "invalid slotId: " + slotId); 1728 } 1729 CatLog.d(LOG_TAG, "getNotificationId, slotId: " + slotId + ", notifyId: " + notifyId); 1730 return notifyId; 1731 } 1732 getItemName(int itemId, int slotId)1733 private String getItemName(int itemId, int slotId) { 1734 Menu menu = mStkContext[slotId].mCurrentCmd.getMenu(); 1735 if (menu == null) { 1736 return null; 1737 } 1738 for (Item item : menu.items) { 1739 if (item.id == itemId) { 1740 return item.text; 1741 } 1742 } 1743 return null; 1744 } 1745 removeMenu(int slotId)1746 private boolean removeMenu(int slotId) { 1747 try { 1748 if (mStkContext[slotId].mCurrentMenu.items.size() == 1 && 1749 mStkContext[slotId].mCurrentMenu.items.get(0) == null) { 1750 mStkContext[slotId].mSetupMenuState = STATE_NOT_EXIST; 1751 return true; 1752 } 1753 } catch (NullPointerException e) { 1754 CatLog.d(LOG_TAG, "Unable to get Menu's items size"); 1755 mStkContext[slotId].mSetupMenuState = STATE_NOT_EXIST; 1756 return true; 1757 } 1758 mStkContext[slotId].mSetupMenuState = STATE_EXIST; 1759 return false; 1760 } 1761 getStkContext(int slotId)1762 StkContext getStkContext(int slotId) { 1763 if (slotId >= 0 && slotId < mSimCount) { 1764 return mStkContext[slotId]; 1765 } else { 1766 CatLog.d(LOG_TAG, "invalid slotId: " + slotId); 1767 return null; 1768 } 1769 } 1770 handleAlphaNotify(Bundle args)1771 private void handleAlphaNotify(Bundle args) { 1772 String alphaString = args.getString(AppInterface.ALPHA_STRING); 1773 1774 CatLog.d(this, "Alpha string received from card: " + alphaString); 1775 Toast toast = Toast.makeText(sInstance, alphaString, Toast.LENGTH_LONG); 1776 toast.setGravity(Gravity.TOP, 0, 0); 1777 toast.show(); 1778 } 1779 } 1780