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