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.Activity; 20 import android.app.AlarmManager; 21 import android.app.AlertDialog; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.Intent; 25 import android.os.Bundle; 26 import android.os.SystemClock; 27 import android.telephony.SubscriptionManager; 28 import android.text.TextUtils; 29 import android.view.LayoutInflater; 30 import android.view.View; 31 import android.widget.ImageView; 32 import android.widget.TextView; 33 34 import com.android.internal.telephony.cat.CatLog; 35 import com.android.internal.telephony.cat.TextMessage; 36 import com.android.internal.telephony.util.TelephonyUtils; 37 38 /** 39 * AlertDialog used for DISPLAY TEXT commands. 40 * 41 */ 42 public class StkDialogActivity extends Activity { 43 // members 44 private static final String LOG_TAG = 45 new Object(){}.getClass().getEnclosingClass().getSimpleName(); 46 TextMessage mTextMsg = null; 47 private int mSlotId = -1; 48 private StkAppService appService = StkAppService.getInstance(); 49 // Determines whether Terminal Response (TR) has been sent 50 private boolean mIsResponseSent = false; 51 // Utilize AlarmManager for real-time countdown 52 private static final String DIALOG_ALARM_TAG = LOG_TAG; 53 private static final long NO_DIALOG_ALARM = -1; 54 private long mAlarmTime = NO_DIALOG_ALARM; 55 56 // Keys for saving the state of the dialog in the bundle 57 private static final String TEXT_KEY = "text"; 58 private static final String ALARM_TIME_KEY = "alarm_time"; 59 private static final String RESPONSE_SENT_KEY = "response_sent"; 60 private static final String SLOT_ID_KEY = "slotid"; 61 62 63 private AlertDialog mAlertDialog; 64 65 @Override onCreate(Bundle savedInstanceState)66 protected void onCreate(Bundle savedInstanceState) { 67 super.onCreate(savedInstanceState); 68 69 CatLog.d(LOG_TAG, "onCreate, sim id: " + mSlotId); 70 71 // appService can be null if this activity is automatically recreated by the system 72 // with the saved instance state right after the phone process is killed. 73 if (appService == null) { 74 CatLog.d(LOG_TAG, "onCreate - appService is null"); 75 finish(); 76 return; 77 } 78 79 // New Dialog is created - set to no response sent 80 mIsResponseSent = false; 81 82 AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); 83 84 alertDialogBuilder.setPositiveButton(R.string.button_ok, new 85 DialogInterface.OnClickListener() { 86 @Override 87 public void onClick(DialogInterface dialog, int id) { 88 CatLog.d(LOG_TAG, "OK Clicked!, mSlotId: " + mSlotId); 89 sendResponse(StkAppService.RES_ID_CONFIRM, true); 90 } 91 }); 92 93 alertDialogBuilder.setNegativeButton(R.string.button_cancel, new 94 DialogInterface.OnClickListener() { 95 @Override 96 public void onClick(DialogInterface dialog,int id) { 97 CatLog.d(LOG_TAG, "Cancel Clicked!, mSlotId: " + mSlotId); 98 sendResponse(StkAppService.RES_ID_CONFIRM, false); 99 } 100 }); 101 102 alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() { 103 @Override 104 public void onCancel(DialogInterface dialog) { 105 CatLog.d(LOG_TAG, "Moving backward!, mSlotId: " + mSlotId); 106 sendResponse(StkAppService.RES_ID_BACKWARD); 107 } 108 }); 109 110 alertDialogBuilder.create(); 111 112 initFromIntent(getIntent()); 113 if (mTextMsg == null) { 114 finish(); 115 return; 116 } 117 118 if (!mTextMsg.responseNeeded) { 119 alertDialogBuilder.setNegativeButton(null, null); 120 // Register the instance of this activity because the dialog displayed for DISPLAY TEXT 121 // command with an immediate response object should disappear when the terminal receives 122 // a subsequent proactive command containing display data. 123 appService.getStkContext(mSlotId).setImmediateDialogInstance(this); 124 } else { 125 appService.getStkContext(mSlotId).setPendingDialogInstance(this); 126 } 127 128 alertDialogBuilder.setTitle(mTextMsg.title); 129 130 LayoutInflater inflater = this.getLayoutInflater(); 131 View dialogView = inflater.inflate(R.layout.stk_msg_dialog, null); 132 alertDialogBuilder.setView(dialogView); 133 TextView tv = (TextView) dialogView.findViewById(R.id.message); 134 ImageView iv = (ImageView) dialogView.findViewById(R.id.icon); 135 136 if (mTextMsg.icon != null) { 137 iv.setImageBitmap(mTextMsg.icon); 138 } else { 139 iv.setVisibility(View.GONE); 140 } 141 142 // Per spec, only set text if the icon is not provided or not self-explanatory 143 if ((mTextMsg.icon == null || !mTextMsg.iconSelfExplanatory) 144 && !TextUtils.isEmpty(mTextMsg.text)) { 145 tv.setText(mTextMsg.text); 146 } else { 147 tv.setVisibility(View.GONE); 148 } 149 150 mAlertDialog = alertDialogBuilder.create(); 151 mAlertDialog.setCanceledOnTouchOutside(false); 152 mAlertDialog.show(); 153 } 154 155 @Override onResume()156 public void onResume() { 157 super.onResume(); 158 CatLog.d(LOG_TAG, "onResume - mIsResponseSent[" + mIsResponseSent + 159 "], sim id: " + mSlotId); 160 /* 161 * If the userClear flag is set and dialogduration is set to 0, the display Text 162 * should be displayed to user forever until some high priority event occurs 163 * (incoming call, MMI code execution etc as mentioned under section 164 * ETSI 102.223, 6.4.1) 165 */ 166 if (StkApp.calculateDurationInMilis(mTextMsg.duration) == 0 && 167 !mTextMsg.responseNeeded && mTextMsg.userClear) { 168 CatLog.d(LOG_TAG, "User should clear text..showing message forever"); 169 return; 170 } 171 172 appService.setDisplayTextDlgVisibility(true, mSlotId); 173 174 /* 175 * When another activity takes the foreground, we do not want the Terminal 176 * Response timer to be restarted when our activity resumes. Hence we will 177 * check if there is an existing timer, and resume it. In this way we will 178 * inform the SIM in correct time when there is no response from the User 179 * to a dialog. 180 */ 181 if (mAlarmTime == NO_DIALOG_ALARM) { 182 startTimeOut(); 183 } 184 } 185 186 @Override onPause()187 public void onPause() { 188 super.onPause(); 189 CatLog.d(LOG_TAG, "onPause, sim id: " + mSlotId); 190 appService.setDisplayTextDlgVisibility(false, mSlotId); 191 192 /* 193 * do not cancel the timer here cancelTimeOut(). If any higher/lower 194 * priority events such as incoming call, new sms, screen off intent, 195 * notification alerts, user actions such as 'User moving to another activtiy' 196 * etc.. occur during Display Text ongoing session, 197 * this activity would receive 'onPause()' event resulting in 198 * cancellation of the timer. As a result no terminal response is 199 * sent to the card. 200 */ 201 } 202 203 @Override onStart()204 protected void onStart() { 205 CatLog.d(LOG_TAG, "onStart, sim id: " + mSlotId); 206 super.onStart(); 207 } 208 209 @Override onStop()210 public void onStop() { 211 super.onStop(); 212 CatLog.d(LOG_TAG, "onStop - before Send CONFIRM false mIsResponseSent[" + 213 mIsResponseSent + "], sim id: " + mSlotId); 214 } 215 216 @Override onDestroy()217 public void onDestroy() { 218 super.onDestroy(); 219 CatLog.d(LOG_TAG, "onDestroy - mIsResponseSent[" + mIsResponseSent + 220 "], sim id: " + mSlotId); 221 222 if (mAlertDialog != null && mAlertDialog.isShowing()) { 223 mAlertDialog.dismiss(); 224 mAlertDialog = null; 225 } 226 227 if (appService == null) { 228 return; 229 } 230 // if dialog activity is finished by stkappservice 231 // when receiving OP_LAUNCH_APP from the other SIM, we can not send TR here 232 // , since the dialog cmd is waiting user to process. 233 if (!isChangingConfigurations()) { 234 if (!mIsResponseSent && appService != null && !appService.isDialogPending(mSlotId)) { 235 sendResponse(StkAppService.RES_ID_CONFIRM, false); 236 } 237 } 238 cancelTimeOut(); 239 } 240 241 @Override onSaveInstanceState(Bundle outState)242 public void onSaveInstanceState(Bundle outState) { 243 super.onSaveInstanceState(outState); 244 245 CatLog.d(LOG_TAG, "onSaveInstanceState"); 246 247 outState.putParcelable(TEXT_KEY, mTextMsg); 248 outState.putBoolean(RESPONSE_SENT_KEY, mIsResponseSent); 249 outState.putLong(ALARM_TIME_KEY, mAlarmTime); 250 outState.putInt(SLOT_ID_KEY, mSlotId); 251 } 252 253 @Override onRestoreInstanceState(Bundle savedInstanceState)254 public void onRestoreInstanceState(Bundle savedInstanceState) { 255 super.onRestoreInstanceState(savedInstanceState); 256 257 CatLog.d(LOG_TAG, "onRestoreInstanceState"); 258 259 mTextMsg = savedInstanceState.getParcelable(TEXT_KEY); 260 mIsResponseSent = savedInstanceState.getBoolean(RESPONSE_SENT_KEY); 261 mAlarmTime = savedInstanceState.getLong(ALARM_TIME_KEY, NO_DIALOG_ALARM); 262 mSlotId = savedInstanceState.getInt(SLOT_ID_KEY); 263 264 if (mAlarmTime != NO_DIALOG_ALARM) { 265 startTimeOut(); 266 } 267 268 } 269 270 @Override onNewIntent(Intent intent)271 protected void onNewIntent(Intent intent) { 272 CatLog.d(LOG_TAG, "onNewIntent - updating the same Dialog box"); 273 setIntent(intent); 274 } 275 276 @Override finish()277 public void finish() { 278 super.finish(); 279 // Unregister the instance for DISPLAY TEXT command with an immediate response object 280 // as it is unnecessary to ask the service to finish this anymore. 281 if ((appService != null) && (mTextMsg != null) && !mTextMsg.responseNeeded) { 282 if (SubscriptionManager.isValidSlotIndex(mSlotId)) { 283 appService.getStkContext(mSlotId).setImmediateDialogInstance(null); 284 } 285 } 286 } 287 sendResponse(int resId, boolean confirmed)288 private void sendResponse(int resId, boolean confirmed) { 289 cancelTimeOut(); 290 291 if (mSlotId == -1) { 292 CatLog.d(LOG_TAG, "sim id is invalid"); 293 return; 294 } 295 296 if (StkAppService.getInstance() == null) { 297 CatLog.d(LOG_TAG, "Ignore response: id is " + resId); 298 return; 299 } 300 301 CatLog.d(LOG_TAG, "sendResponse resID[" + resId + "] confirmed[" + confirmed + "]"); 302 303 if (mTextMsg.responseNeeded) { 304 Bundle args = new Bundle(); 305 args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE); 306 args.putInt(StkAppService.SLOT_ID, mSlotId); 307 args.putInt(StkAppService.RES_ID, resId); 308 args.putBoolean(StkAppService.CONFIRMATION, confirmed); 309 startService(new Intent(this, StkAppService.class).putExtras(args)); 310 mIsResponseSent = true; 311 } 312 if (!isFinishing()) { 313 finish(); 314 } 315 316 } 317 sendResponse(int resId)318 private void sendResponse(int resId) { 319 sendResponse(resId, true); 320 } 321 initFromIntent(Intent intent)322 private void initFromIntent(Intent intent) { 323 324 if (intent != null) { 325 mTextMsg = intent.getParcelableExtra("TEXT"); 326 mSlotId = intent.getIntExtra(StkAppService.SLOT_ID, -1); 327 } else { 328 finish(); 329 } 330 331 CatLog.d(LOG_TAG, "initFromIntent - [" + (TelephonyUtils.IS_DEBUGGABLE ? mTextMsg : "********") 332 + "], slot id: " + mSlotId); 333 } 334 cancelTimeOut()335 private void cancelTimeOut() { 336 if (mAlarmTime != NO_DIALOG_ALARM) { 337 CatLog.d(LOG_TAG, "cancelTimeOut - slot id: " + mSlotId); 338 AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 339 am.cancel(mAlarmListener); 340 mAlarmTime = NO_DIALOG_ALARM; 341 } 342 } 343 startTimeOut()344 private void startTimeOut() { 345 // No need to set alarm if device sent TERMINAL RESPONSE already 346 // and it is required to wait for user to clear the message. 347 if (mIsResponseSent || (mTextMsg.userClear && !mTextMsg.responseNeeded)) { 348 return; 349 } 350 351 if (mAlarmTime == NO_DIALOG_ALARM) { 352 int duration = StkApp.calculateDurationInMilis(mTextMsg.duration); 353 // If no duration is specified, the timeout set by the terminal manufacturer is applied. 354 if (duration == 0) { 355 if (mTextMsg.userClear) { 356 duration = StkApp.DISP_TEXT_WAIT_FOR_USER_TIMEOUT; 357 } else { 358 duration = StkApp.DISP_TEXT_CLEAR_AFTER_DELAY_TIMEOUT; 359 } 360 } 361 mAlarmTime = SystemClock.elapsedRealtime() + duration; 362 } 363 364 CatLog.d(LOG_TAG, "startTimeOut: " + mAlarmTime + "ms, slot id: " + mSlotId); 365 AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 366 am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, mAlarmTime, DIALOG_ALARM_TAG, 367 mAlarmListener, null); 368 } 369 370 private final AlarmManager.OnAlarmListener mAlarmListener = 371 new AlarmManager.OnAlarmListener() { 372 @Override 373 public void onAlarm() { 374 CatLog.d(LOG_TAG, "The alarm time is reached"); 375 mAlarmTime = NO_DIALOG_ALARM; 376 sendResponse(StkAppService.RES_ID_TIMEOUT); 377 } 378 }; 379 } 380