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