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 com.android.internal.telephony.cat.CatLog; 20 import com.android.internal.telephony.cat.TextMessage; 21 22 import android.app.Activity; 23 import android.app.AlarmManager; 24 import android.app.AlertDialog; 25 import android.app.PendingIntent; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.BroadcastReceiver; 29 import android.content.Context; 30 import android.content.DialogInterface; 31 import android.view.KeyEvent; 32 33 import android.os.Bundle; 34 import android.os.SystemClock; 35 36 /** 37 * AlertDialog used for DISPLAY TEXT commands. 38 * 39 */ 40 public class StkDialogActivity extends Activity { 41 // members 42 private static final String className = new Object(){}.getClass().getEnclosingClass().getName(); 43 private static final String LOG_TAG = className.substring(className.lastIndexOf('.') + 1); 44 TextMessage mTextMsg = null; 45 private int mSlotId = -1; 46 private StkAppService appService = StkAppService.getInstance(); 47 // Determines whether Terminal Response (TR) has been sent 48 private boolean mIsResponseSent = false; 49 private Context mContext; 50 // Utilize AlarmManager for real-time countdown 51 private PendingIntent mTimeoutIntent; 52 private AlarmManager mAlarmManager; 53 private final static String ALARM_TIMEOUT = "com.android.stk.DIALOG_ALARM_TIMEOUT"; 54 55 //keys) for saving the state of the dialog in the icicle 56 private static final String TEXT = "text"; 57 58 private AlertDialog.Builder alertDialogBuilder; 59 60 @Override onCreate(Bundle icicle)61 protected void onCreate(Bundle icicle) { 62 super.onCreate(icicle); 63 64 CatLog.d(LOG_TAG, "onCreate, sim id: " + mSlotId); 65 66 // appService can be null if this activity is automatically recreated by the system 67 // with the saved instance state right after the phone process is killed. 68 if (appService == null) { 69 CatLog.d(LOG_TAG, "onCreate - appService is null"); 70 finish(); 71 return; 72 } 73 74 // New Dialog is created - set to no response sent 75 mIsResponseSent = false; 76 77 alertDialogBuilder = new AlertDialog.Builder(this); 78 79 alertDialogBuilder.setPositiveButton(R.string.button_ok, new 80 DialogInterface.OnClickListener() { 81 @Override 82 public void onClick(DialogInterface dialog, int id) { 83 CatLog.d(LOG_TAG, "OK Clicked!, mSlotId: " + mSlotId); 84 cancelTimeOut(); 85 sendResponse(StkAppService.RES_ID_CONFIRM, true); 86 finish(); 87 } 88 }); 89 90 alertDialogBuilder.setNegativeButton(R.string.button_cancel, new 91 DialogInterface.OnClickListener() { 92 @Override 93 public void onClick(DialogInterface dialog,int id) { 94 CatLog.d(LOG_TAG, "Cancel Clicked!, mSlotId: " + mSlotId); 95 cancelTimeOut(); 96 sendResponse(StkAppService.RES_ID_CONFIRM, false); 97 finish(); 98 } 99 }); 100 alertDialogBuilder.create(); 101 102 mContext = getBaseContext(); 103 IntentFilter intentFilter = new IntentFilter(); 104 intentFilter.addAction(ALARM_TIMEOUT); 105 mContext.registerReceiver(mBroadcastReceiver, intentFilter); 106 mAlarmManager =(AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); 107 108 setFinishOnTouchOutside(false); 109 } 110 111 @Override onKeyDown(int keyCode, KeyEvent event)112 public boolean onKeyDown(int keyCode, KeyEvent event) { 113 switch (keyCode) { 114 case KeyEvent.KEYCODE_BACK: 115 CatLog.d(LOG_TAG, "onKeyDown - KEYCODE_BACK"); 116 cancelTimeOut(); 117 sendResponse(StkAppService.RES_ID_BACKWARD); 118 finish(); 119 break; 120 } 121 return false; 122 } 123 124 @Override onResume()125 public void onResume() { 126 super.onResume(); 127 CatLog.d(LOG_TAG, "onResume - mIsResponseSent[" + mIsResponseSent + 128 "], sim id: " + mSlotId); 129 130 initFromIntent(getIntent()); 131 if (mTextMsg == null) { 132 finish(); 133 return; 134 } 135 136 alertDialogBuilder.setTitle(mTextMsg.title); 137 138 if (!(mTextMsg.iconSelfExplanatory && mTextMsg.icon != null)) { 139 alertDialogBuilder.setMessage(mTextMsg.text); 140 } 141 alertDialogBuilder.show(); 142 143 /* 144 * If the userClear flag is set and dialogduration is set to 0, the display Text 145 * should be displayed to user forever until some high priority event occurs 146 * (incoming call, MMI code execution etc as mentioned under section 147 * ETSI 102.223, 6.4.1) 148 */ 149 if (StkApp.calculateDurationInMilis(mTextMsg.duration) == 0 && 150 !mTextMsg.responseNeeded && mTextMsg.userClear) { 151 CatLog.d(LOG_TAG, "User should clear text..showing message forever"); 152 return; 153 } 154 155 appService.setDisplayTextDlgVisibility(true, mSlotId); 156 157 /* 158 * When another activity takes the foreground, we do not want the Terminal 159 * Response timer to be restarted when our activity resumes. Hence we will 160 * check if there is an existing timer, and resume it. In this way we will 161 * inform the SIM in correct time when there is no response from the User 162 * to a dialog. 163 */ 164 if (mTimeoutIntent != null) { 165 CatLog.d(LOG_TAG, "Pending Alarm! Let it finish counting down..."); 166 } 167 else { 168 CatLog.d(LOG_TAG, "No Pending Alarm! OK to start timer..."); 169 startTimeOut(mTextMsg.userClear); 170 } 171 } 172 173 @Override onPause()174 public void onPause() { 175 super.onPause(); 176 CatLog.d(LOG_TAG, "onPause, sim id: " + mSlotId); 177 appService.setDisplayTextDlgVisibility(false, mSlotId); 178 179 /* 180 * do not cancel the timer here cancelTimeOut(). If any higher/lower 181 * priority events such as incoming call, new sms, screen off intent, 182 * notification alerts, user actions such as 'User moving to another activtiy' 183 * etc.. occur during Display Text ongoing session, 184 * this activity would receive 'onPause()' event resulting in 185 * cancellation of the timer. As a result no terminal response is 186 * sent to the card. 187 */ 188 } 189 190 @Override onStart()191 protected void onStart() { 192 CatLog.d(LOG_TAG, "onStart, sim id: " + mSlotId); 193 super.onStart(); 194 } 195 196 @Override onStop()197 public void onStop() { 198 super.onStop(); 199 CatLog.d(LOG_TAG, "onStop - before Send CONFIRM false mIsResponseSent[" + 200 mIsResponseSent + "], sim id: " + mSlotId); 201 if (!mTextMsg.responseNeeded) { 202 return; 203 } 204 if (!mIsResponseSent) { 205 appService.getStkContext(mSlotId).setPendingDialogInstance(this); 206 } else { 207 CatLog.d(LOG_TAG, "finish."); 208 appService.getStkContext(mSlotId).setPendingDialogInstance(null); 209 cancelTimeOut(); 210 finish(); 211 } 212 } 213 214 @Override onDestroy()215 public void onDestroy() { 216 super.onDestroy(); 217 CatLog.d(LOG_TAG, "onDestroy - mIsResponseSent[" + mIsResponseSent + 218 "], sim id: " + mSlotId); 219 if (appService == null) { 220 return; 221 } 222 // if dialog activity is finished by stkappservice 223 // when receiving OP_LAUNCH_APP from the other SIM, we can not send TR here 224 // , since the dialog cmd is waiting user to process. 225 if (!mIsResponseSent && !appService.isDialogPending(mSlotId)) { 226 sendResponse(StkAppService.RES_ID_CONFIRM, false); 227 } 228 cancelTimeOut(); 229 // Cleanup broadcast receivers to avoid leaks 230 if (mBroadcastReceiver != null) { 231 unregisterReceiver(mBroadcastReceiver); 232 } 233 } 234 235 @Override onSaveInstanceState(Bundle outState)236 public void onSaveInstanceState(Bundle outState) { 237 CatLog.d(LOG_TAG, "onSaveInstanceState"); 238 239 super.onSaveInstanceState(outState); 240 241 outState.putParcelable(TEXT, mTextMsg); 242 } 243 244 @Override onRestoreInstanceState(Bundle savedInstanceState)245 public void onRestoreInstanceState(Bundle savedInstanceState) { 246 super.onRestoreInstanceState(savedInstanceState); 247 248 mTextMsg = savedInstanceState.getParcelable(TEXT); 249 CatLog.d(LOG_TAG, "onRestoreInstanceState - [" + mTextMsg + "]"); 250 } 251 252 @Override onNewIntent(Intent intent)253 protected void onNewIntent(Intent intent) { 254 CatLog.d(LOG_TAG, "onNewIntent - updating the same Dialog box"); 255 setIntent(intent); 256 } 257 sendResponse(int resId, boolean confirmed)258 private void sendResponse(int resId, boolean confirmed) { 259 if (mSlotId == -1) { 260 CatLog.d(LOG_TAG, "sim id is invalid"); 261 return; 262 } 263 264 if (StkAppService.getInstance() == null) { 265 CatLog.d(LOG_TAG, "Ignore response: id is " + resId); 266 return; 267 } 268 269 CatLog.d(LOG_TAG, "sendResponse resID[" + resId + "] confirmed[" + confirmed + "]"); 270 271 if (mTextMsg.responseNeeded) { 272 Bundle args = new Bundle(); 273 args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE); 274 args.putInt(StkAppService.SLOT_ID, mSlotId); 275 args.putInt(StkAppService.RES_ID, resId); 276 args.putBoolean(StkAppService.CONFIRMATION, confirmed); 277 startService(new Intent(this, StkAppService.class).putExtras(args)); 278 mIsResponseSent = true; 279 } 280 } 281 sendResponse(int resId)282 private void sendResponse(int resId) { 283 sendResponse(resId, true); 284 } 285 initFromIntent(Intent intent)286 private void initFromIntent(Intent intent) { 287 288 if (intent != null) { 289 mTextMsg = intent.getParcelableExtra("TEXT"); 290 mSlotId = intent.getIntExtra(StkAppService.SLOT_ID, -1); 291 } else { 292 finish(); 293 } 294 295 CatLog.d(LOG_TAG, "initFromIntent - [" + mTextMsg + "], sim id: " + mSlotId); 296 } 297 cancelTimeOut()298 private void cancelTimeOut() { 299 CatLog.d(LOG_TAG, "cancelTimeOut: " + mSlotId); 300 if (mTimeoutIntent != null) { 301 mAlarmManager.cancel(mTimeoutIntent); 302 mTimeoutIntent = null; 303 } 304 } 305 startTimeOut(boolean waitForUserToClear)306 private void startTimeOut(boolean waitForUserToClear) { 307 308 // Reset timeout. 309 cancelTimeOut(); 310 int dialogDuration = StkApp.calculateDurationInMilis(mTextMsg.duration); 311 // If duration is specified, this has priority. If not, set timeout 312 // according to condition given by the card. 313 if (mTextMsg.userClear == true && mTextMsg.responseNeeded == false) { 314 return; 315 } else { 316 // userClear = false. will disappear after a while. 317 if (dialogDuration == 0) { 318 if (waitForUserToClear) { 319 dialogDuration = StkApp.DISP_TEXT_WAIT_FOR_USER_TIMEOUT; 320 } else { 321 dialogDuration = StkApp.DISP_TEXT_CLEAR_AFTER_DELAY_TIMEOUT; 322 } 323 } 324 CatLog.d(LOG_TAG, "startTimeOut: " + mSlotId); 325 Intent mAlarmIntent = new Intent(ALARM_TIMEOUT); 326 mAlarmIntent.putExtra(StkAppService.SLOT_ID, mSlotId); 327 mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, mAlarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); 328 329 // Try to use a more stringent timer not affected by system sleep. 330 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { 331 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 332 SystemClock.elapsedRealtime() + dialogDuration, mTimeoutIntent); 333 } 334 else { 335 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 336 SystemClock.elapsedRealtime() + dialogDuration, mTimeoutIntent); 337 } 338 } 339 } 340 341 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 342 @Override public void onReceive(Context context, Intent intent) { 343 String action = intent.getAction(); 344 int slotID = intent.getIntExtra(StkAppService.SLOT_ID, 0); 345 346 if (action == null || slotID != mSlotId) return; 347 CatLog.d(LOG_TAG, "onReceive, action=" + action + ", sim id: " + slotID); 348 if (action.equals(ALARM_TIMEOUT)) { 349 CatLog.d(LOG_TAG, "ALARM_TIMEOUT rcvd"); 350 mTimeoutIntent = null; 351 sendResponse(StkAppService.RES_ID_TIMEOUT); 352 finish(); 353 } 354 } 355 }; 356 } 357