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