• 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.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