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