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