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 17package com.android.stk; 18 19import android.app.ActivityManager; 20import android.app.ActivityManager.RunningTaskInfo; 21import android.app.AlertDialog; 22import android.app.Notification; 23import android.app.NotificationManager; 24import android.app.PendingIntent; 25import android.app.Service; 26import android.app.Activity; 27import android.app.ActivityManager; 28import android.app.ActivityManager.RecentTaskInfo; 29import android.app.ActivityManager.RunningAppProcessInfo; 30import android.content.ComponentName; 31import android.content.Context; 32import android.content.DialogInterface; 33import android.content.Intent; 34import android.content.res.Configuration; 35import android.graphics.Bitmap; 36import android.graphics.BitmapFactory; 37import android.net.Uri; 38import android.os.Bundle; 39import android.os.Handler; 40import android.os.IBinder; 41import android.os.Looper; 42import android.os.Message; 43import android.os.PersistableBundle; 44import android.os.PowerManager; 45import android.os.SystemProperties; 46import android.provider.Settings; 47import android.telephony.CarrierConfigManager; 48import android.telephony.TelephonyManager; 49import android.text.TextUtils; 50import android.view.Gravity; 51import android.view.LayoutInflater; 52import android.view.View; 53import android.view.Window; 54import android.view.WindowManager; 55import android.widget.ImageView; 56import android.widget.RemoteViews; 57import android.widget.TextView; 58import android.widget.Toast; 59import android.content.BroadcastReceiver; 60import android.content.IntentFilter; 61import android.content.pm.ApplicationInfo; 62import android.content.pm.PackageManager.NameNotFoundException; 63 64import com.android.internal.telephony.cat.AppInterface; 65import com.android.internal.telephony.cat.LaunchBrowserMode; 66import com.android.internal.telephony.cat.Menu; 67import com.android.internal.telephony.cat.Item; 68import com.android.internal.telephony.cat.Input; 69import com.android.internal.telephony.cat.ResultCode; 70import com.android.internal.telephony.cat.CatCmdMessage; 71import com.android.internal.telephony.cat.CatCmdMessage.BrowserSettings; 72import com.android.internal.telephony.cat.CatCmdMessage.SetupEventListSettings; 73import com.android.internal.telephony.cat.CatLog; 74import com.android.internal.telephony.cat.CatResponseMessage; 75import com.android.internal.telephony.cat.TextMessage; 76import com.android.internal.telephony.uicc.IccRefreshResponse; 77import com.android.internal.telephony.uicc.IccCardStatus.CardState; 78import com.android.internal.telephony.PhoneConstants; 79import com.android.internal.telephony.TelephonyIntents; 80import com.android.internal.telephony.IccCardConstants; 81import com.android.internal.telephony.uicc.UiccController; 82import com.android.internal.telephony.GsmAlphabet; 83import com.android.internal.telephony.cat.CatService; 84 85import java.util.Iterator; 86import java.util.LinkedList; 87import java.lang.System; 88import java.util.List; 89 90import static com.android.internal.telephony.cat.CatCmdMessage. 91 SetupEventListConstants.IDLE_SCREEN_AVAILABLE_EVENT; 92import static com.android.internal.telephony.cat.CatCmdMessage. 93 SetupEventListConstants.LANGUAGE_SELECTION_EVENT; 94 95/** 96 * SIM toolkit application level service. Interacts with Telephopny messages, 97 * application's launch and user input from STK UI elements. 98 * 99 */ 100public class StkAppService extends Service implements Runnable { 101 102 // members 103 protected class StkContext { 104 protected CatCmdMessage mMainCmd = null; 105 protected CatCmdMessage mCurrentCmd = null; 106 protected CatCmdMessage mCurrentMenuCmd = null; 107 protected Menu mCurrentMenu = null; 108 protected String lastSelectedItem = null; 109 protected boolean mMenuIsVisible = false; 110 protected boolean mIsInputPending = false; 111 protected boolean mIsMenuPending = false; 112 protected boolean mIsDialogPending = false; 113 protected boolean responseNeeded = true; 114 protected boolean launchBrowser = false; 115 protected BrowserSettings mBrowserSettings = null; 116 protected LinkedList<DelayedCmd> mCmdsQ = null; 117 protected boolean mCmdInProgress = false; 118 protected int mStkServiceState = STATE_UNKNOWN; 119 protected int mSetupMenuState = STATE_UNKNOWN; 120 protected int mMenuState = StkMenuActivity.STATE_INIT; 121 protected int mOpCode = -1; 122 private Activity mActivityInstance = null; 123 private Activity mDialogInstance = null; 124 private Activity mMainActivityInstance = null; 125 private int mSlotId = 0; 126 private SetupEventListSettings mSetupEventListSettings = null; 127 private boolean mClearSelectItem = false; 128 private boolean mDisplayTextDlgIsVisibile = false; 129 private CatCmdMessage mCurrentSetupEventCmd = null; 130 private CatCmdMessage mIdleModeTextCmd = null; 131 final synchronized void setPendingActivityInstance(Activity act) { 132 CatLog.d(this, "setPendingActivityInstance act : " + mSlotId + ", " + act); 133 callSetActivityInstMsg(OP_SET_ACT_INST, mSlotId, act); 134 } 135 final synchronized Activity getPendingActivityInstance() { 136 CatLog.d(this, "getPendingActivityInstance act : " + mSlotId + ", " + 137 mActivityInstance); 138 return mActivityInstance; 139 } 140 final synchronized void setPendingDialogInstance(Activity act) { 141 CatLog.d(this, "setPendingDialogInstance act : " + mSlotId + ", " + act); 142 callSetActivityInstMsg(OP_SET_DAL_INST, mSlotId, act); 143 } 144 final synchronized Activity getPendingDialogInstance() { 145 CatLog.d(this, "getPendingDialogInstance act : " + mSlotId + ", " + 146 mDialogInstance); 147 return mDialogInstance; 148 } 149 final synchronized void setMainActivityInstance(Activity act) { 150 CatLog.d(this, "setMainActivityInstance act : " + mSlotId + ", " + act); 151 callSetActivityInstMsg(OP_SET_MAINACT_INST, mSlotId, act); 152 } 153 final synchronized Activity getMainActivityInstance() { 154 CatLog.d(this, "getMainActivityInstance act : " + mSlotId + ", " + 155 mMainActivityInstance); 156 return mMainActivityInstance; 157 } 158 } 159 160 private volatile Looper mServiceLooper; 161 private volatile ServiceHandler mServiceHandler; 162 private Context mContext = null; 163 private NotificationManager mNotificationManager = null; 164 static StkAppService sInstance = null; 165 private AppInterface[] mStkService = null; 166 private StkContext[] mStkContext = null; 167 private int mSimCount = 0; 168 private PowerManager mPowerManager = null; 169 private StkCmdReceiver mStkCmdReceiver = null; 170 171 // Used for setting FLAG_ACTIVITY_NO_USER_ACTION when 172 // creating an intent. 173 private enum InitiatedByUserAction { 174 yes, // The action was started via a user initiated action 175 unknown, // Not known for sure if user initated the action 176 } 177 178 // constants 179 static final String OPCODE = "op"; 180 static final String CMD_MSG = "cmd message"; 181 static final String RES_ID = "response id"; 182 static final String MENU_SELECTION = "menu selection"; 183 static final String INPUT = "input"; 184 static final String HELP = "help"; 185 static final String CONFIRMATION = "confirm"; 186 static final String CHOICE = "choice"; 187 static final String SLOT_ID = "SLOT_ID"; 188 static final String STK_CMD = "STK CMD"; 189 static final String STK_DIALOG_URI = "stk://com.android.stk/dialog/"; 190 static final String STK_MENU_URI = "stk://com.android.stk/menu/"; 191 static final String STK_INPUT_URI = "stk://com.android.stk/input/"; 192 static final String STK_TONE_URI = "stk://com.android.stk/tone/"; 193 194 // These below constants are used for SETUP_EVENT_LIST 195 static final String SETUP_EVENT_TYPE = "event"; 196 static final String SETUP_EVENT_CAUSE = "cause"; 197 198 // operations ids for different service functionality. 199 static final int OP_CMD = 1; 200 static final int OP_RESPONSE = 2; 201 static final int OP_LAUNCH_APP = 3; 202 static final int OP_END_SESSION = 4; 203 static final int OP_BOOT_COMPLETED = 5; 204 private static final int OP_DELAYED_MSG = 6; 205 static final int OP_CARD_STATUS_CHANGED = 7; 206 static final int OP_SET_ACT_INST = 8; 207 static final int OP_SET_DAL_INST = 9; 208 static final int OP_SET_MAINACT_INST = 10; 209 static final int OP_LOCALE_CHANGED = 11; 210 static final int OP_ALPHA_NOTIFY = 12; 211 static final int OP_IDLE_SCREEN = 13; 212 213 //Invalid SetupEvent 214 static final int INVALID_SETUP_EVENT = 0xFF; 215 216 // Response ids 217 static final int RES_ID_MENU_SELECTION = 11; 218 static final int RES_ID_INPUT = 12; 219 static final int RES_ID_CONFIRM = 13; 220 static final int RES_ID_DONE = 14; 221 static final int RES_ID_CHOICE = 15; 222 223 static final int RES_ID_TIMEOUT = 20; 224 static final int RES_ID_BACKWARD = 21; 225 static final int RES_ID_END_SESSION = 22; 226 static final int RES_ID_EXIT = 23; 227 228 static final int YES = 1; 229 static final int NO = 0; 230 231 static final int STATE_UNKNOWN = -1; 232 static final int STATE_NOT_EXIST = 0; 233 static final int STATE_EXIST = 1; 234 235 private static final String PACKAGE_NAME = "com.android.stk"; 236 private static final String STK_MENU_ACTIVITY_NAME = PACKAGE_NAME + ".StkMenuActivity"; 237 private static final String STK_INPUT_ACTIVITY_NAME = PACKAGE_NAME + ".StkInputActivity"; 238 private static final String STK_DIALOG_ACTIVITY_NAME = PACKAGE_NAME + ".StkDialogActivity"; 239 // Notification id used to display Idle Mode text in NotificationManager. 240 private static final int STK_NOTIFICATION_ID = 333; 241 private static final String LOG_TAG = new Object(){}.getClass().getEnclosingClass().getName(); 242 243 // Inner class used for queuing telephony messages (proactive commands, 244 // session end) while the service is busy processing a previous message. 245 private class DelayedCmd { 246 // members 247 int id; 248 CatCmdMessage msg; 249 int slotId; 250 251 DelayedCmd(int id, CatCmdMessage msg, int slotId) { 252 this.id = id; 253 this.msg = msg; 254 this.slotId = slotId; 255 } 256 } 257 258 // system property to set the STK specific default url for launch browser proactive cmds 259 private static final String STK_BROWSER_DEFAULT_URL_SYSPROP = "persist.radio.stk.default_url"; 260 261 @Override 262 public void onCreate() { 263 CatLog.d(LOG_TAG, "onCreate()+"); 264 // Initialize members 265 int i = 0; 266 mContext = getBaseContext(); 267 mSimCount = TelephonyManager.from(mContext).getSimCount(); 268 CatLog.d(LOG_TAG, "simCount: " + mSimCount); 269 mStkService = new AppInterface[mSimCount]; 270 mStkContext = new StkContext[mSimCount]; 271 mPowerManager = (PowerManager)getSystemService(Context.POWER_SERVICE); 272 mStkCmdReceiver = new StkCmdReceiver(); 273 registerReceiver(mStkCmdReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); 274 for (i = 0; i < mSimCount; i++) { 275 CatLog.d(LOG_TAG, "slotId: " + i); 276 mStkService[i] = CatService.getInstance(i); 277 mStkContext[i] = new StkContext(); 278 mStkContext[i].mSlotId = i; 279 mStkContext[i].mCmdsQ = new LinkedList<DelayedCmd>(); 280 } 281 282 Thread serviceThread = new Thread(null, this, "Stk App Service"); 283 serviceThread.start(); 284 mNotificationManager = (NotificationManager) mContext 285 .getSystemService(Context.NOTIFICATION_SERVICE); 286 sInstance = this; 287 } 288 289 @Override 290 public void onStart(Intent intent, int startId) { 291 if (intent == null) { 292 CatLog.d(LOG_TAG, "StkAppService onStart intent is null so return"); 293 return; 294 } 295 296 Bundle args = intent.getExtras(); 297 if (args == null) { 298 CatLog.d(LOG_TAG, "StkAppService onStart args is null so return"); 299 return; 300 } 301 302 int op = args.getInt(OPCODE); 303 int slotId = 0; 304 int i = 0; 305 if (op != OP_BOOT_COMPLETED) { 306 slotId = args.getInt(SLOT_ID); 307 } 308 CatLog.d(LOG_TAG, "onStart sim id: " + slotId + ", op: " + op + ", *****"); 309 if ((slotId >= 0 && slotId < mSimCount) && mStkService[slotId] == null) { 310 mStkService[slotId] = CatService.getInstance(slotId); 311 if (mStkService[slotId] == null) { 312 CatLog.d(LOG_TAG, "mStkService is: " + mStkContext[slotId].mStkServiceState); 313 mStkContext[slotId].mStkServiceState = STATE_NOT_EXIST; 314 //Check other StkService state. 315 //If all StkServices are not available, stop itself and uninstall apk. 316 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) { 317 if (i != slotId 318 && (mStkContext[i].mStkServiceState == STATE_UNKNOWN 319 || mStkContext[i].mStkServiceState == STATE_EXIST)) { 320 break; 321 } 322 } 323 } else { 324 mStkContext[slotId].mStkServiceState = STATE_EXIST; 325 } 326 if (i == mSimCount) { 327 stopSelf(); 328 StkAppInstaller.unInstall(mContext); 329 return; 330 } 331 } 332 333 waitForLooper(); 334 335 Message msg = mServiceHandler.obtainMessage(); 336 msg.arg1 = op; 337 msg.arg2 = slotId; 338 switch(msg.arg1) { 339 case OP_CMD: 340 msg.obj = args.getParcelable(CMD_MSG); 341 break; 342 case OP_RESPONSE: 343 case OP_CARD_STATUS_CHANGED: 344 case OP_LOCALE_CHANGED: 345 case OP_ALPHA_NOTIFY: 346 case OP_IDLE_SCREEN: 347 msg.obj = args; 348 /* falls through */ 349 case OP_LAUNCH_APP: 350 case OP_END_SESSION: 351 case OP_BOOT_COMPLETED: 352 break; 353 default: 354 return; 355 } 356 mServiceHandler.sendMessage(msg); 357 } 358 359 @Override 360 public void onDestroy() { 361 CatLog.d(LOG_TAG, "onDestroy()"); 362 if (mStkCmdReceiver != null) { 363 unregisterReceiver(mStkCmdReceiver); 364 mStkCmdReceiver = null; 365 } 366 mPowerManager = null; 367 waitForLooper(); 368 mServiceLooper.quit(); 369 } 370 371 @Override 372 public IBinder onBind(Intent intent) { 373 return null; 374 } 375 376 public void run() { 377 Looper.prepare(); 378 379 mServiceLooper = Looper.myLooper(); 380 mServiceHandler = new ServiceHandler(); 381 382 Looper.loop(); 383 } 384 385 /* 386 * Package api used by StkMenuActivity to indicate if its on the foreground. 387 */ 388 void indicateMenuVisibility(boolean visibility, int slotId) { 389 if (slotId >= 0 && slotId < mSimCount) { 390 mStkContext[slotId].mMenuIsVisible = visibility; 391 } 392 } 393 394 /* 395 * Package api used by StkDialogActivity to indicate if its on the foreground. 396 */ 397 void setDisplayTextDlgVisibility(boolean visibility, int slotId) { 398 if (slotId >= 0 && slotId < mSimCount) { 399 mStkContext[slotId].mDisplayTextDlgIsVisibile = visibility; 400 } 401 } 402 403 boolean isInputPending(int slotId) { 404 if (slotId >= 0 && slotId < mSimCount) { 405 CatLog.d(LOG_TAG, "isInputFinishBySrv: " + mStkContext[slotId].mIsInputPending); 406 return mStkContext[slotId].mIsInputPending; 407 } 408 return false; 409 } 410 411 boolean isMenuPending(int slotId) { 412 if (slotId >= 0 && slotId < mSimCount) { 413 CatLog.d(LOG_TAG, "isMenuPending: " + mStkContext[slotId].mIsMenuPending); 414 return mStkContext[slotId].mIsMenuPending; 415 } 416 return false; 417 } 418 419 boolean isDialogPending(int slotId) { 420 if (slotId >= 0 && slotId < mSimCount) { 421 CatLog.d(LOG_TAG, "isDialogPending: " + mStkContext[slotId].mIsDialogPending); 422 return mStkContext[slotId].mIsDialogPending; 423 } 424 return false; 425 } 426 427 /* 428 * Package api used by StkMenuActivity to get its Menu parameter. 429 */ 430 Menu getMenu(int slotId) { 431 CatLog.d(LOG_TAG, "StkAppService, getMenu, sim id: " + slotId); 432 if (slotId >=0 && slotId < mSimCount) { 433 return mStkContext[slotId].mCurrentMenu; 434 } else { 435 return null; 436 } 437 } 438 439 /* 440 * Package api used by StkMenuActivity to get its Main Menu parameter. 441 */ 442 Menu getMainMenu(int slotId) { 443 CatLog.d(LOG_TAG, "StkAppService, getMainMenu, sim id: " + slotId); 444 if (slotId >=0 && slotId < mSimCount && (mStkContext[slotId].mMainCmd != null)) { 445 return mStkContext[slotId].mMainCmd.getMenu(); 446 } else { 447 return null; 448 } 449 } 450 451 /* 452 * Package api used by UI Activities and Dialogs to communicate directly 453 * with the service to deliver state information and parameters. 454 */ 455 static StkAppService getInstance() { 456 return sInstance; 457 } 458 459 private void waitForLooper() { 460 while (mServiceHandler == null) { 461 synchronized (this) { 462 try { 463 wait(100); 464 } catch (InterruptedException e) { 465 } 466 } 467 } 468 } 469 470 private final class ServiceHandler extends Handler { 471 @Override 472 public void handleMessage(Message msg) { 473 if(null == msg) { 474 CatLog.d(LOG_TAG, "ServiceHandler handleMessage msg is null"); 475 return; 476 } 477 int opcode = msg.arg1; 478 int slotId = msg.arg2; 479 480 CatLog.d(LOG_TAG, "handleMessage opcode[" + opcode + "], sim id[" + slotId + "]"); 481 if (opcode == OP_CMD && msg.obj != null && 482 ((CatCmdMessage)msg.obj).getCmdType()!= null) { 483 CatLog.d(LOG_TAG, "cmdName[" + ((CatCmdMessage)msg.obj).getCmdType().name() + "]"); 484 } 485 mStkContext[slotId].mOpCode = opcode; 486 switch (opcode) { 487 case OP_LAUNCH_APP: 488 if (mStkContext[slotId].mMainCmd == null) { 489 CatLog.d(LOG_TAG, "mMainCmd is null"); 490 // nothing todo when no SET UP MENU command didn't arrive. 491 return; 492 } 493 CatLog.d(LOG_TAG, "handleMessage OP_LAUNCH_APP - mCmdInProgress[" + 494 mStkContext[slotId].mCmdInProgress + "]"); 495 496 //If there is a pending activity for the slot id, 497 //just finish it and create a new one to handle the pending command. 498 cleanUpInstanceStackBySlot(slotId); 499 500 CatLog.d(LOG_TAG, "Current cmd type: " + 501 mStkContext[slotId].mCurrentCmd.getCmdType()); 502 //Restore the last command from stack by slot id. 503 restoreInstanceFromStackBySlot(slotId); 504 break; 505 case OP_CMD: 506 CatLog.d(LOG_TAG, "[OP_CMD]"); 507 CatCmdMessage cmdMsg = (CatCmdMessage) msg.obj; 508 // There are two types of commands: 509 // 1. Interactive - user's response is required. 510 // 2. Informative - display a message, no interaction with the user. 511 // 512 // Informative commands can be handled immediately without any delay. 513 // Interactive commands can't override each other. So if a command 514 // is already in progress, we need to queue the next command until 515 // the user has responded or a timeout expired. 516 if (!isCmdInteractive(cmdMsg)) { 517 handleCmd(cmdMsg, slotId); 518 } else { 519 if (!mStkContext[slotId].mCmdInProgress) { 520 mStkContext[slotId].mCmdInProgress = true; 521 handleCmd((CatCmdMessage) msg.obj, slotId); 522 } else { 523 CatLog.d(LOG_TAG, "[Interactive][in progress]"); 524 mStkContext[slotId].mCmdsQ.addLast(new DelayedCmd(OP_CMD, 525 (CatCmdMessage) msg.obj, slotId)); 526 } 527 } 528 break; 529 case OP_RESPONSE: 530 handleCmdResponse((Bundle) msg.obj, slotId); 531 // call delayed commands if needed. 532 if (mStkContext[slotId].mCmdsQ.size() != 0) { 533 callDelayedMsg(slotId); 534 } else { 535 mStkContext[slotId].mCmdInProgress = false; 536 } 537 break; 538 case OP_END_SESSION: 539 if (!mStkContext[slotId].mCmdInProgress) { 540 mStkContext[slotId].mCmdInProgress = true; 541 handleSessionEnd(slotId); 542 } else { 543 mStkContext[slotId].mCmdsQ.addLast( 544 new DelayedCmd(OP_END_SESSION, null, slotId)); 545 } 546 break; 547 case OP_BOOT_COMPLETED: 548 CatLog.d(LOG_TAG, " OP_BOOT_COMPLETED"); 549 int i = 0; 550 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) { 551 if (mStkContext[i].mMainCmd != null) { 552 break; 553 } 554 } 555 if (i == mSimCount) { 556 StkAppInstaller.unInstall(mContext); 557 } 558 break; 559 case OP_DELAYED_MSG: 560 handleDelayedCmd(slotId); 561 break; 562 case OP_CARD_STATUS_CHANGED: 563 CatLog.d(LOG_TAG, "Card/Icc Status change received"); 564 handleCardStatusChangeAndIccRefresh((Bundle) msg.obj, slotId); 565 break; 566 case OP_SET_ACT_INST: 567 Activity act = new Activity(); 568 act = (Activity) msg.obj; 569 CatLog.d(LOG_TAG, "Set activity instance. " + act); 570 mStkContext[slotId].mActivityInstance = act; 571 break; 572 case OP_SET_DAL_INST: 573 Activity dal = new Activity(); 574 CatLog.d(LOG_TAG, "Set dialog instance. " + dal); 575 dal = (Activity) msg.obj; 576 mStkContext[slotId].mDialogInstance = dal; 577 break; 578 case OP_SET_MAINACT_INST: 579 Activity mainAct = new Activity(); 580 mainAct = (Activity) msg.obj; 581 CatLog.d(LOG_TAG, "Set activity instance. " + mainAct); 582 mStkContext[slotId].mMainActivityInstance = mainAct; 583 break; 584 case OP_LOCALE_CHANGED: 585 CatLog.d(this, "Locale Changed"); 586 for (int slot = PhoneConstants.SIM_ID_1; slot < mSimCount; slot++) { 587 checkForSetupEvent(LANGUAGE_SELECTION_EVENT, (Bundle) msg.obj, slot); 588 } 589 break; 590 case OP_ALPHA_NOTIFY: 591 handleAlphaNotify((Bundle) msg.obj); 592 break; 593 case OP_IDLE_SCREEN: 594 for (int slot = 0; slot < mSimCount; slot++) { 595 if (mStkContext[slot] != null) { 596 handleIdleScreen(slot); 597 } 598 } 599 break; 600 } 601 } 602 603 private void handleCardStatusChangeAndIccRefresh(Bundle args, int slotId) { 604 boolean cardStatus = args.getBoolean(AppInterface.CARD_STATUS); 605 606 CatLog.d(LOG_TAG, "CardStatus: " + cardStatus); 607 if (cardStatus == false) { 608 CatLog.d(LOG_TAG, "CARD is ABSENT"); 609 // Uninstall STKAPP, Clear Idle text, Stop StkAppService 610 mNotificationManager.cancel(getNotificationId(slotId)); 611 if (isAllOtherCardsAbsent(slotId)) { 612 CatLog.d(LOG_TAG, "All CARDs are ABSENT"); 613 StkAppInstaller.unInstall(mContext); 614 stopSelf(); 615 } 616 } else { 617 IccRefreshResponse state = new IccRefreshResponse(); 618 state.refreshResult = args.getInt(AppInterface.REFRESH_RESULT); 619 620 CatLog.d(LOG_TAG, "Icc Refresh Result: "+ state.refreshResult); 621 if ((state.refreshResult == IccRefreshResponse.REFRESH_RESULT_INIT) || 622 (state.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET)) { 623 // Clear Idle Text 624 mNotificationManager.cancel(getNotificationId(slotId)); 625 } 626 627 if (state.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET) { 628 // Uninstall STkmenu 629 if (isAllOtherCardsAbsent(slotId)) { 630 StkAppInstaller.unInstall(mContext); 631 } 632 mStkContext[slotId].mCurrentMenu = null; 633 mStkContext[slotId].mMainCmd = null; 634 } 635 } 636 } 637 } 638 /* 639 * Check if all SIMs are absent except the id of slot equals "slotId". 640 */ 641 private boolean isAllOtherCardsAbsent(int slotId) { 642 TelephonyManager mTm = (TelephonyManager) mContext.getSystemService( 643 Context.TELEPHONY_SERVICE); 644 int i = 0; 645 646 for (i = 0; i < mSimCount; i++) { 647 if (i != slotId && mTm.hasIccCard(i)) { 648 break; 649 } 650 } 651 if (i == mSimCount) { 652 return true; 653 } else { 654 return false; 655 } 656 } 657 658 /* 659 * If the device is not in an interactive state, we can assume 660 * that the screen is idle. 661 */ 662 private boolean isScreenIdle() { 663 return (!mPowerManager.isInteractive()); 664 } 665 666 private void handleIdleScreen(int slotId) { 667 668 // If the idle screen event is present in the list need to send the 669 // response to SIM. 670 CatLog.d(this, "Need to send IDLE SCREEN Available event to SIM"); 671 checkForSetupEvent(IDLE_SCREEN_AVAILABLE_EVENT, null, slotId); 672 673 if (mStkContext[slotId].mIdleModeTextCmd != null) { 674 launchIdleText(slotId); 675 } 676 } 677 678 private void sendScreenBusyResponse(int slotId) { 679 if (mStkContext[slotId].mCurrentCmd == null) { 680 return; 681 } 682 CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentCmd); 683 CatLog.d(this, "SCREEN_BUSY"); 684 resMsg.setResultCode(ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS); 685 mStkService[slotId].onCmdResponse(resMsg); 686 if (mStkContext[slotId].mCmdsQ.size() != 0) { 687 callDelayedMsg(slotId); 688 } else { 689 mStkContext[slotId].mCmdInProgress = false; 690 } 691 } 692 693 private void sendResponse(int resId, int slotId, boolean confirm) { 694 Message msg = mServiceHandler.obtainMessage(); 695 msg.arg1 = OP_RESPONSE; 696 msg.arg2 = slotId; 697 Bundle args = new Bundle(); 698 args.putInt(StkAppService.RES_ID, resId); 699 args.putBoolean(StkAppService.CONFIRMATION, confirm); 700 msg.obj = args; 701 mServiceHandler.sendMessage(msg); 702 } 703 704 private boolean isCmdInteractive(CatCmdMessage cmd) { 705 switch (cmd.getCmdType()) { 706 case SEND_DTMF: 707 case SEND_SMS: 708 case SEND_SS: 709 case SEND_USSD: 710 case SET_UP_IDLE_MODE_TEXT: 711 case SET_UP_MENU: 712 case CLOSE_CHANNEL: 713 case RECEIVE_DATA: 714 case SEND_DATA: 715 case SET_UP_EVENT_LIST: 716 return false; 717 } 718 719 return true; 720 } 721 722 private void handleDelayedCmd(int slotId) { 723 CatLog.d(LOG_TAG, "handleDelayedCmd, slotId: " + slotId); 724 if (mStkContext[slotId].mCmdsQ.size() != 0) { 725 DelayedCmd cmd = mStkContext[slotId].mCmdsQ.poll(); 726 if (cmd != null) { 727 CatLog.d(LOG_TAG, "handleDelayedCmd - queue size: " + 728 mStkContext[slotId].mCmdsQ.size() + 729 " id: " + cmd.id + "sim id: " + cmd.slotId); 730 switch (cmd.id) { 731 case OP_CMD: 732 handleCmd(cmd.msg, cmd.slotId); 733 break; 734 case OP_END_SESSION: 735 handleSessionEnd(cmd.slotId); 736 break; 737 } 738 } 739 } 740 } 741 742 private void callDelayedMsg(int slotId) { 743 Message msg = mServiceHandler.obtainMessage(); 744 msg.arg1 = OP_DELAYED_MSG; 745 msg.arg2 = slotId; 746 mServiceHandler.sendMessage(msg); 747 } 748 749 private void callSetActivityInstMsg(int inst_type, int slotId, Object obj) { 750 Message msg = mServiceHandler.obtainMessage(); 751 msg.obj = obj; 752 msg.arg1 = inst_type; 753 msg.arg2 = slotId; 754 mServiceHandler.sendMessage(msg); 755 } 756 757 private void handleSessionEnd(int slotId) { 758 // We should finish all pending activity if receiving END SESSION command. 759 cleanUpInstanceStackBySlot(slotId); 760 761 mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd; 762 CatLog.d(LOG_TAG, "[handleSessionEnd] - mCurrentCmd changed to mMainCmd!."); 763 mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mMainCmd; 764 CatLog.d(LOG_TAG, "slotId: " + slotId + ", mMenuState: " + 765 mStkContext[slotId].mMenuState); 766 767 mStkContext[slotId].mIsInputPending = false; 768 mStkContext[slotId].mIsMenuPending = false; 769 mStkContext[slotId].mIsDialogPending = false; 770 771 if (mStkContext[slotId].mMainCmd == null) { 772 CatLog.d(LOG_TAG, "[handleSessionEnd][mMainCmd is null!]"); 773 } 774 mStkContext[slotId].lastSelectedItem = null; 775 // In case of SET UP MENU command which removed the app, don't 776 // update the current menu member. 777 if (mStkContext[slotId].mCurrentMenu != null && mStkContext[slotId].mMainCmd != null) { 778 mStkContext[slotId].mCurrentMenu = mStkContext[slotId].mMainCmd.getMenu(); 779 } 780 CatLog.d(LOG_TAG, "[handleSessionEnd][mMenuState]" + mStkContext[slotId].mMenuIsVisible); 781 // In mutiple instance architecture, the main menu for slotId will be finished when user 782 // goes to the Stk menu of the other SIM. So, we should launch a new instance for the 783 // main menu if the main menu instance has been finished. 784 // If the current menu is secondary menu, we should launch main menu. 785 if (StkMenuActivity.STATE_SECONDARY == mStkContext[slotId].mMenuState) { 786 launchMenuActivity(null, slotId); 787 } 788 if (mStkContext[slotId].mCmdsQ.size() != 0) { 789 callDelayedMsg(slotId); 790 } else { 791 mStkContext[slotId].mCmdInProgress = false; 792 } 793 // In case a launch browser command was just confirmed, launch that url. 794 if (mStkContext[slotId].launchBrowser) { 795 mStkContext[slotId].launchBrowser = false; 796 launchBrowser(mStkContext[slotId].mBrowserSettings); 797 } 798 } 799 800 // returns true if any Stk related activity already has focus on the screen 801 private boolean isTopOfStack() { 802 ActivityManager mAcivityManager = (ActivityManager) mContext 803 .getSystemService(ACTIVITY_SERVICE); 804 String currentPackageName = mAcivityManager.getRunningTasks(1).get(0).topActivity 805 .getPackageName(); 806 if (null != currentPackageName) { 807 return currentPackageName.equals(PACKAGE_NAME); 808 } 809 810 return false; 811 } 812 813 /** 814 * Get the boolean config from carrier config manager. 815 * 816 * @param context the context to get carrier service 817 * @param key config key defined in CarrierConfigManager 818 * @return boolean value of corresponding key. 819 */ 820 private static boolean getBooleanCarrierConfig(Context context, String key) { 821 CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService( 822 Context.CARRIER_CONFIG_SERVICE); 823 PersistableBundle b = null; 824 if (configManager != null) { 825 b = configManager.getConfig(); 826 } 827 if (b != null) { 828 return b.getBoolean(key); 829 } else { 830 // Return static default defined in CarrierConfigManager. 831 return CarrierConfigManager.getDefaultConfig().getBoolean(key); 832 } 833 } 834 835 private void handleCmd(CatCmdMessage cmdMsg, int slotId) { 836 837 if (cmdMsg == null) { 838 return; 839 } 840 // save local reference for state tracking. 841 mStkContext[slotId].mCurrentCmd = cmdMsg; 842 boolean waitForUsersResponse = true; 843 844 mStkContext[slotId].mIsInputPending = false; 845 mStkContext[slotId].mIsMenuPending = false; 846 mStkContext[slotId].mIsDialogPending = false; 847 848 CatLog.d(LOG_TAG,"[handleCmd]" + cmdMsg.getCmdType().name()); 849 switch (cmdMsg.getCmdType()) { 850 case DISPLAY_TEXT: 851 TextMessage msg = cmdMsg.geTextMessage(); 852 waitForUsersResponse = msg.responseNeeded; 853 if (mStkContext[slotId].lastSelectedItem != null) { 854 msg.title = mStkContext[slotId].lastSelectedItem; 855 } else if (mStkContext[slotId].mMainCmd != null){ 856 msg.title = mStkContext[slotId].mMainCmd.getMenu().title; 857 } else { 858 // TODO: get the carrier name from the SIM 859 msg.title = ""; 860 } 861 //If we receive a low priority Display Text and the device is 862 // not displaying any STK related activity and the screen is not idle 863 // ( that is, device is in an interactive state), then send a screen busy 864 // terminal response. Otherwise display the message. The existing 865 // displayed message shall be updated with the new display text 866 // proactive command (Refer to ETSI TS 102 384 section 27.22.4.1.4.4.2). 867 if (!(msg.isHighPriority || mStkContext[slotId].mMenuIsVisible 868 || mStkContext[slotId].mDisplayTextDlgIsVisibile || isTopOfStack())) { 869 if(!isScreenIdle()) { 870 CatLog.d(LOG_TAG, "Screen is not idle"); 871 sendScreenBusyResponse(slotId); 872 } else { 873 launchTextDialog(slotId); 874 } 875 } else { 876 launchTextDialog(slotId); 877 } 878 break; 879 case SELECT_ITEM: 880 CatLog.d(LOG_TAG, "SELECT_ITEM +"); 881 mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mCurrentCmd; 882 mStkContext[slotId].mCurrentMenu = cmdMsg.getMenu(); 883 launchMenuActivity(cmdMsg.getMenu(), slotId); 884 break; 885 case SET_UP_MENU: 886 mStkContext[slotId].mCmdInProgress = false; 887 mStkContext[slotId].mMainCmd = mStkContext[slotId].mCurrentCmd; 888 mStkContext[slotId].mCurrentMenuCmd = mStkContext[slotId].mCurrentCmd; 889 mStkContext[slotId].mCurrentMenu = cmdMsg.getMenu(); 890 CatLog.d(LOG_TAG, "SET_UP_MENU [" + removeMenu(slotId) + "]"); 891 892 if (removeMenu(slotId)) { 893 int i = 0; 894 CatLog.d(LOG_TAG, "removeMenu() - Uninstall App"); 895 mStkContext[slotId].mCurrentMenu = null; 896 mStkContext[slotId].mMainCmd = null; 897 //Check other setup menu state. If all setup menu are removed, uninstall apk. 898 for (i = PhoneConstants.SIM_ID_1; i < mSimCount; i++) { 899 if (i != slotId 900 && (mStkContext[slotId].mSetupMenuState == STATE_UNKNOWN 901 || mStkContext[slotId].mSetupMenuState == STATE_EXIST)) { 902 CatLog.d(LOG_TAG, "Not Uninstall App:" + i + "," 903 + mStkContext[slotId].mSetupMenuState); 904 break; 905 } 906 } 907 if (i == mSimCount) { 908 StkAppInstaller.unInstall(mContext); 909 } 910 } else { 911 CatLog.d(LOG_TAG, "install App"); 912 StkAppInstaller.install(mContext); 913 } 914 if (mStkContext[slotId].mMenuIsVisible) { 915 launchMenuActivity(null, slotId); 916 } 917 break; 918 case GET_INPUT: 919 case GET_INKEY: 920 launchInputActivity(slotId); 921 break; 922 case SET_UP_IDLE_MODE_TEXT: 923 waitForUsersResponse = false; 924 mStkContext[slotId].mIdleModeTextCmd = mStkContext[slotId].mCurrentCmd; 925 TextMessage idleModeText = mStkContext[slotId].mCurrentCmd.geTextMessage(); 926 if (idleModeText == null) { 927 launchIdleText(slotId); 928 mStkContext[slotId].mIdleModeTextCmd = null; 929 } 930 mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd; 931 if ((mStkContext[slotId].mIdleModeTextCmd != null) && isScreenIdle()) { 932 CatLog.d(this, "set up idle mode"); 933 launchIdleText(slotId); 934 } 935 break; 936 case SEND_DTMF: 937 case SEND_SMS: 938 case SEND_SS: 939 case SEND_USSD: 940 case GET_CHANNEL_STATUS: 941 waitForUsersResponse = false; 942 launchEventMessage(slotId); 943 break; 944 case LAUNCH_BROWSER: 945 946 /* Check if Carrier would not want to launch browser */ 947 if (getBooleanCarrierConfig(mContext, 948 CarrierConfigManager.KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL)) { 949 CatLog.d(this, "Browser is not launched as per carrier."); 950 sendResponse(RES_ID_DONE, slotId, true); 951 break; 952 } 953 954 TextMessage alphaId = mStkContext[slotId].mCurrentCmd.geTextMessage(); 955 if ((mStkContext[slotId].mCurrentCmd.getBrowserSettings().mode 956 == LaunchBrowserMode.LAUNCH_IF_NOT_ALREADY_LAUNCHED) && 957 ((alphaId == null) || TextUtils.isEmpty(alphaId.text))) { 958 // don't need user confirmation in this case 959 // just launch the browser or spawn a new tab 960 CatLog.d(this, "Browser mode is: launch if not already launched " + 961 "and user confirmation is not currently needed.\n" + 962 "supressing confirmation dialogue and confirming silently..."); 963 mStkContext[slotId].launchBrowser = true; 964 mStkContext[slotId].mBrowserSettings = 965 mStkContext[slotId].mCurrentCmd.getBrowserSettings(); 966 sendResponse(RES_ID_CONFIRM, slotId, true); 967 } else { 968 launchConfirmationDialog(alphaId, slotId); 969 } 970 break; 971 case SET_UP_CALL: 972 TextMessage mesg = mStkContext[slotId].mCurrentCmd.getCallSettings().confirmMsg; 973 if((mesg != null) && (mesg.text == null || mesg.text.length() == 0)) { 974 mesg.text = getResources().getString(R.string.default_setup_call_msg); 975 } 976 CatLog.d(this, "SET_UP_CALL mesg.text " + mesg.text); 977 launchConfirmationDialog(mesg, slotId); 978 break; 979 case PLAY_TONE: 980 launchToneDialog(slotId); 981 break; 982 case OPEN_CHANNEL: 983 launchOpenChannelDialog(slotId); 984 break; 985 case CLOSE_CHANNEL: 986 case RECEIVE_DATA: 987 case SEND_DATA: 988 TextMessage m = mStkContext[slotId].mCurrentCmd.geTextMessage(); 989 990 if ((m != null) && (m.text == null)) { 991 switch(cmdMsg.getCmdType()) { 992 case CLOSE_CHANNEL: 993 m.text = getResources().getString(R.string.default_close_channel_msg); 994 break; 995 case RECEIVE_DATA: 996 m.text = getResources().getString(R.string.default_receive_data_msg); 997 break; 998 case SEND_DATA: 999 m.text = getResources().getString(R.string.default_send_data_msg); 1000 break; 1001 } 1002 } 1003 /* 1004 * Display indication in the form of a toast to the user if required. 1005 */ 1006 launchEventMessage(slotId); 1007 break; 1008 case SET_UP_EVENT_LIST: 1009 mStkContext[slotId].mSetupEventListSettings = 1010 mStkContext[slotId].mCurrentCmd.getSetEventList(); 1011 mStkContext[slotId].mCurrentSetupEventCmd = mStkContext[slotId].mCurrentCmd; 1012 mStkContext[slotId].mCurrentCmd = mStkContext[slotId].mMainCmd; 1013 if (isScreenIdle()) { 1014 CatLog.d(this," Check if IDLE_SCREEN_AVAILABLE_EVENT is present in List"); 1015 checkForSetupEvent(IDLE_SCREEN_AVAILABLE_EVENT, null, slotId); 1016 } 1017 break; 1018 } 1019 1020 if (!waitForUsersResponse) { 1021 if (mStkContext[slotId].mCmdsQ.size() != 0) { 1022 callDelayedMsg(slotId); 1023 } else { 1024 mStkContext[slotId].mCmdInProgress = false; 1025 } 1026 } 1027 } 1028 1029 private void handleCmdResponse(Bundle args, int slotId) { 1030 CatLog.d(LOG_TAG, "handleCmdResponse, sim id: " + slotId); 1031 if (mStkContext[slotId].mCurrentCmd == null) { 1032 return; 1033 } 1034 1035 if (mStkService[slotId] == null) { 1036 mStkService[slotId] = CatService.getInstance(slotId); 1037 if (mStkService[slotId] == null) { 1038 // This should never happen (we should be responding only to a message 1039 // that arrived from StkService). It has to exist by this time 1040 CatLog.d(LOG_TAG, "Exception! mStkService is null when we need to send response."); 1041 throw new RuntimeException("mStkService is null when we need to send response"); 1042 } 1043 } 1044 1045 CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentCmd); 1046 1047 // set result code 1048 boolean helpRequired = args.getBoolean(HELP, false); 1049 boolean confirmed = false; 1050 1051 switch(args.getInt(RES_ID)) { 1052 case RES_ID_MENU_SELECTION: 1053 CatLog.d(LOG_TAG, "MENU_SELECTION=" + mStkContext[slotId]. 1054 mCurrentMenuCmd.getCmdType()); 1055 int menuSelection = args.getInt(MENU_SELECTION); 1056 switch(mStkContext[slotId].mCurrentMenuCmd.getCmdType()) { 1057 case SET_UP_MENU: 1058 case SELECT_ITEM: 1059 mStkContext[slotId].lastSelectedItem = getItemName(menuSelection, slotId); 1060 if (helpRequired) { 1061 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED); 1062 } else { 1063 resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ? 1064 ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK); 1065 } 1066 resMsg.setMenuSelection(menuSelection); 1067 break; 1068 } 1069 break; 1070 case RES_ID_INPUT: 1071 CatLog.d(LOG_TAG, "RES_ID_INPUT"); 1072 String input = args.getString(INPUT); 1073 if (input != null && (null != mStkContext[slotId].mCurrentCmd.geInput()) && 1074 (mStkContext[slotId].mCurrentCmd.geInput().yesNo)) { 1075 boolean yesNoSelection = input 1076 .equals(StkInputActivity.YES_STR_RESPONSE); 1077 resMsg.setYesNo(yesNoSelection); 1078 } else { 1079 if (helpRequired) { 1080 resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED); 1081 } else { 1082 resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ? 1083 ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK); 1084 resMsg.setInput(input); 1085 } 1086 } 1087 break; 1088 case RES_ID_CONFIRM: 1089 CatLog.d(this, "RES_ID_CONFIRM"); 1090 confirmed = args.getBoolean(CONFIRMATION); 1091 switch (mStkContext[slotId].mCurrentCmd.getCmdType()) { 1092 case DISPLAY_TEXT: 1093 if (confirmed) { 1094 resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed() ? 1095 ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK); 1096 } else { 1097 resMsg.setResultCode(ResultCode.UICC_SESSION_TERM_BY_USER); 1098 } 1099 break; 1100 case LAUNCH_BROWSER: 1101 resMsg.setResultCode(confirmed ? ResultCode.OK 1102 : ResultCode.UICC_SESSION_TERM_BY_USER); 1103 if (confirmed) { 1104 mStkContext[slotId].launchBrowser = true; 1105 mStkContext[slotId].mBrowserSettings = 1106 mStkContext[slotId].mCurrentCmd.getBrowserSettings(); 1107 } 1108 break; 1109 case SET_UP_CALL: 1110 resMsg.setResultCode(ResultCode.OK); 1111 resMsg.setConfirmation(confirmed); 1112 if (confirmed) { 1113 CatLog.d(this, "Going back to mainMenu before starting a call."); 1114 launchMenuActivity(null, slotId); 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 */ 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 */ 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 */ 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 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 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 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 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 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 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 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 1449 private void launchEventMessage(int slotId) { 1450 launchEventMessage(slotId, mStkContext[slotId].mCurrentCmd.geTextMessage()); 1451 } 1452 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 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 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(10000); 1559 } catch (InterruptedException e) {} 1560 } 1561 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 1578 final Notification.Builder notificationBuilder = new Notification.Builder( 1579 StkAppService.this); 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 private void launchToneDialog(int slotId) { 1611 Intent newIntent = new Intent(this, ToneDialog.class); 1612 String uriString = STK_TONE_URI + slotId; 1613 Uri uriData = Uri.parse(uriString); 1614 //Set unique URI to create a new instance of activity for different slotId. 1615 CatLog.d(LOG_TAG, "launchToneDialog, slotId: " + slotId); 1616 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 1617 | Intent.FLAG_ACTIVITY_NO_HISTORY 1618 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 1619 | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId)); 1620 newIntent.putExtra("TEXT", mStkContext[slotId].mCurrentCmd.geTextMessage()); 1621 newIntent.putExtra("TONE", mStkContext[slotId].mCurrentCmd.getToneSettings()); 1622 newIntent.putExtra(SLOT_ID, slotId); 1623 newIntent.setData(uriData); 1624 startActivity(newIntent); 1625 } 1626 1627 private void launchOpenChannelDialog(final int slotId) { 1628 TextMessage msg = mStkContext[slotId].mCurrentCmd.geTextMessage(); 1629 if (msg == null) { 1630 CatLog.d(LOG_TAG, "msg is null, return here"); 1631 return; 1632 } 1633 1634 msg.title = getResources().getString(R.string.stk_dialog_title); 1635 if (msg.text == null) { 1636 msg.text = getResources().getString(R.string.default_open_channel_msg); 1637 } 1638 1639 final AlertDialog dialog = new AlertDialog.Builder(mContext) 1640 .setIconAttribute(android.R.attr.alertDialogIcon) 1641 .setTitle(msg.title) 1642 .setMessage(msg.text) 1643 .setCancelable(false) 1644 .setPositiveButton(getResources().getString(R.string.stk_dialog_accept), 1645 new DialogInterface.OnClickListener() { 1646 public void onClick(DialogInterface dialog, int which) { 1647 Bundle args = new Bundle(); 1648 args.putInt(RES_ID, RES_ID_CHOICE); 1649 args.putInt(CHOICE, YES); 1650 Message message = mServiceHandler.obtainMessage(); 1651 message.arg1 = OP_RESPONSE; 1652 message.arg2 = slotId; 1653 message.obj = args; 1654 mServiceHandler.sendMessage(message); 1655 } 1656 }) 1657 .setNegativeButton(getResources().getString(R.string.stk_dialog_reject), 1658 new DialogInterface.OnClickListener() { 1659 public void onClick(DialogInterface dialog, int which) { 1660 Bundle args = new Bundle(); 1661 args.putInt(RES_ID, RES_ID_CHOICE); 1662 args.putInt(CHOICE, NO); 1663 Message message = mServiceHandler.obtainMessage(); 1664 message.arg1 = OP_RESPONSE; 1665 message.arg2 = slotId; 1666 message.obj = args; 1667 mServiceHandler.sendMessage(message); 1668 } 1669 }) 1670 .create(); 1671 1672 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1673 if (!mContext.getResources().getBoolean( 1674 com.android.internal.R.bool.config_sf_slowBlur)) { 1675 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); 1676 } 1677 1678 dialog.show(); 1679 } 1680 1681 private void launchTransientEventMessage(int slotId) { 1682 TextMessage msg = mStkContext[slotId].mCurrentCmd.geTextMessage(); 1683 if (msg == null) { 1684 CatLog.d(LOG_TAG, "msg is null, return here"); 1685 return; 1686 } 1687 1688 msg.title = getResources().getString(R.string.stk_dialog_title); 1689 1690 final AlertDialog dialog = new AlertDialog.Builder(mContext) 1691 .setIconAttribute(android.R.attr.alertDialogIcon) 1692 .setTitle(msg.title) 1693 .setMessage(msg.text) 1694 .setCancelable(false) 1695 .setPositiveButton(getResources().getString(android.R.string.ok), 1696 new DialogInterface.OnClickListener() { 1697 public void onClick(DialogInterface dialog, int which) { 1698 } 1699 }) 1700 .create(); 1701 1702 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1703 if (!mContext.getResources().getBoolean( 1704 com.android.internal.R.bool.config_sf_slowBlur)) { 1705 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); 1706 } 1707 1708 dialog.show(); 1709 } 1710 1711 private int getNotificationId(int slotId) { 1712 int notifyId = STK_NOTIFICATION_ID; 1713 if (slotId >= 0 && slotId < mSimCount) { 1714 notifyId += slotId; 1715 } else { 1716 CatLog.d(LOG_TAG, "invalid slotId: " + slotId); 1717 } 1718 CatLog.d(LOG_TAG, "getNotificationId, slotId: " + slotId + ", notifyId: " + notifyId); 1719 return notifyId; 1720 } 1721 1722 private String getItemName(int itemId, int slotId) { 1723 Menu menu = mStkContext[slotId].mCurrentCmd.getMenu(); 1724 if (menu == null) { 1725 return null; 1726 } 1727 for (Item item : menu.items) { 1728 if (item.id == itemId) { 1729 return item.text; 1730 } 1731 } 1732 return null; 1733 } 1734 1735 private boolean removeMenu(int slotId) { 1736 try { 1737 if (mStkContext[slotId].mCurrentMenu.items.size() == 1 && 1738 mStkContext[slotId].mCurrentMenu.items.get(0) == null) { 1739 mStkContext[slotId].mSetupMenuState = STATE_NOT_EXIST; 1740 return true; 1741 } 1742 } catch (NullPointerException e) { 1743 CatLog.d(LOG_TAG, "Unable to get Menu's items size"); 1744 mStkContext[slotId].mSetupMenuState = STATE_NOT_EXIST; 1745 return true; 1746 } 1747 mStkContext[slotId].mSetupMenuState = STATE_EXIST; 1748 return false; 1749 } 1750 1751 StkContext getStkContext(int slotId) { 1752 if (slotId >= 0 && slotId < mSimCount) { 1753 return mStkContext[slotId]; 1754 } else { 1755 CatLog.d(LOG_TAG, "invalid slotId: " + slotId); 1756 return null; 1757 } 1758 } 1759 1760 private void handleAlphaNotify(Bundle args) { 1761 String alphaString = args.getString(AppInterface.ALPHA_STRING); 1762 1763 CatLog.d(this, "Alpha string received from card: " + alphaString); 1764 Toast toast = Toast.makeText(sInstance, alphaString, Toast.LENGTH_LONG); 1765 toast.setGravity(Gravity.TOP, 0, 0); 1766 toast.show(); 1767 } 1768} 1769