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