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