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