• 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 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 
32 import android.os.Build;
33 import android.os.Bundle;
34 import android.os.SystemClock;
35 import android.telephony.SubscriptionManager;
36 import android.text.TextUtils;
37 import android.view.LayoutInflater;
38 import android.view.View;
39 import android.widget.ImageView;
40 import android.widget.TextView;
41 
42 /**
43  * AlertDialog used for DISPLAY TEXT commands.
44  *
45  */
46 public class StkDialogActivity extends Activity {
47     // members
48     private static final String className = new Object(){}.getClass().getEnclosingClass().getName();
49     private static final String LOG_TAG = className.substring(className.lastIndexOf('.') + 1);
50     TextMessage mTextMsg = null;
51     private int mSlotId = -1;
52     private StkAppService appService = StkAppService.getInstance();
53     // Determines whether Terminal Response (TR) has been sent
54     private boolean mIsResponseSent = false;
55     private Context mContext;
56     // Utilize AlarmManager for real-time countdown
57     private PendingIntent mTimeoutIntent;
58     private AlarmManager mAlarmManager;
59     private final static String ALARM_TIMEOUT = "com.android.stk.DIALOG_ALARM_TIMEOUT";
60 
61     // Keys for saving the state of the dialog in the bundle
62     private static final String TEXT_KEY = "text";
63     private static final String TIMEOUT_INTENT_KEY = "timeout";
64     private static final String SLOT_ID_KEY = "slotid";
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                         cancelTimeOut();
93                         sendResponse(StkAppService.RES_ID_CONFIRM, true);
94                         finish();
95                     }
96                 });
97 
98         alertDialogBuilder.setNegativeButton(R.string.button_cancel, new
99                 DialogInterface.OnClickListener() {
100                     @Override
101                     public void onClick(DialogInterface dialog,int id) {
102                         CatLog.d(LOG_TAG, "Cancel Clicked!, mSlotId: " + mSlotId);
103                         cancelTimeOut();
104                         sendResponse(StkAppService.RES_ID_CONFIRM, false);
105                         finish();
106                     }
107                 });
108 
109         alertDialogBuilder.setOnCancelListener(new DialogInterface.OnCancelListener() {
110                     @Override
111                     public void onCancel(DialogInterface dialog) {
112                         CatLog.d(LOG_TAG, "Moving backward!, mSlotId: " + mSlotId);
113                         cancelTimeOut();
114                         sendResponse(StkAppService.RES_ID_BACKWARD);
115                         finish();
116                     }
117                 });
118 
119         alertDialogBuilder.create();
120 
121         initFromIntent(getIntent());
122         if (mTextMsg == null) {
123             finish();
124             return;
125         }
126 
127         if (!mTextMsg.responseNeeded) {
128             alertDialogBuilder.setNegativeButton(null, null);
129             // Register the instance of this activity because the dialog displayed for DISPLAY TEXT
130             // command with an immediate response object should disappear when the terminal receives
131             // a subsequent proactive command containing display data.
132             appService.getStkContext(mSlotId).setImmediateDialogInstance(this);
133         }
134 
135         alertDialogBuilder.setTitle(mTextMsg.title);
136 
137         LayoutInflater inflater = this.getLayoutInflater();
138         View dialogView = inflater.inflate(R.layout.stk_msg_dialog, null);
139         alertDialogBuilder.setView(dialogView);
140         TextView tv = (TextView) dialogView.findViewById(R.id.message);
141         ImageView iv = (ImageView) dialogView.findViewById(R.id.icon);
142 
143         if (mTextMsg.icon != null) {
144             iv.setImageBitmap(mTextMsg.icon);
145         } else {
146             iv.setVisibility(View.GONE);
147         }
148 
149         // Per spec, only set text if the icon is not provided or not self-explanatory
150         if ((mTextMsg.icon == null || !mTextMsg.iconSelfExplanatory)
151                 && !TextUtils.isEmpty(mTextMsg.text)) {
152             tv.setText(mTextMsg.text);
153         } else {
154             tv.setVisibility(View.GONE);
155         }
156 
157         mAlertDialog = alertDialogBuilder.create();
158         mAlertDialog.setCanceledOnTouchOutside(false);
159         mAlertDialog.show();
160 
161         mContext = getBaseContext();
162         IntentFilter intentFilter = new IntentFilter();
163         intentFilter.addAction(ALARM_TIMEOUT);
164         mContext.registerReceiver(mBroadcastReceiver, intentFilter);
165         mAlarmManager =(AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
166     }
167 
168     @Override
onResume()169     public void onResume() {
170         super.onResume();
171         CatLog.d(LOG_TAG, "onResume - mIsResponseSent[" + mIsResponseSent +
172                 "], sim id: " + mSlotId);
173 
174         /*
175          * If the userClear flag is set and dialogduration is set to 0, the display Text
176          * should be displayed to user forever until some high priority event occurs
177          * (incoming call, MMI code execution etc as mentioned under section
178          * ETSI 102.223, 6.4.1)
179          */
180         if (StkApp.calculateDurationInMilis(mTextMsg.duration) == 0 &&
181                 !mTextMsg.responseNeeded && mTextMsg.userClear) {
182             CatLog.d(LOG_TAG, "User should clear text..showing message forever");
183             return;
184         }
185 
186         appService.setDisplayTextDlgVisibility(true, mSlotId);
187 
188         /*
189          * When another activity takes the foreground, we do not want the Terminal
190          * Response timer to be restarted when our activity resumes. Hence we will
191          * check if there is an existing timer, and resume it. In this way we will
192          * inform the SIM in correct time when there is no response from the User
193          * to a dialog.
194          */
195         if (mTimeoutIntent != null) {
196             CatLog.d(LOG_TAG, "Pending Alarm! Let it finish counting down...");
197         }
198         else {
199             CatLog.d(LOG_TAG, "No Pending Alarm! OK to start timer...");
200             startTimeOut(mTextMsg.userClear);
201         }
202     }
203 
204     @Override
onPause()205     public void onPause() {
206         super.onPause();
207         CatLog.d(LOG_TAG, "onPause, sim id: " + mSlotId);
208         appService.setDisplayTextDlgVisibility(false, mSlotId);
209 
210         /*
211          * do not cancel the timer here cancelTimeOut(). If any higher/lower
212          * priority events such as incoming call, new sms, screen off intent,
213          * notification alerts, user actions such as 'User moving to another activtiy'
214          * etc.. occur during Display Text ongoing session,
215          * this activity would receive 'onPause()' event resulting in
216          * cancellation of the timer. As a result no terminal response is
217          * sent to the card.
218          */
219     }
220 
221     @Override
onStart()222     protected void onStart() {
223         CatLog.d(LOG_TAG, "onStart, sim id: " + mSlotId);
224         super.onStart();
225     }
226 
227     @Override
onStop()228     public void onStop() {
229         super.onStop();
230         CatLog.d(LOG_TAG, "onStop - before Send CONFIRM false mIsResponseSent[" +
231                 mIsResponseSent + "], sim id: " + mSlotId);
232 
233         // Avoid calling finish() or setPendingDialogInstance()
234         // if the activity is being restarted now.
235         if (isChangingConfigurations()) {
236             return;
237         }
238 
239         if (!mTextMsg.responseNeeded) {
240             return;
241         }
242         if (!mIsResponseSent) {
243             appService.getStkContext(mSlotId).setPendingDialogInstance(this);
244         } else {
245             CatLog.d(LOG_TAG, "finish.");
246             appService.getStkContext(mSlotId).setPendingDialogInstance(null);
247             cancelTimeOut();
248             finish();
249         }
250     }
251 
252     @Override
onDestroy()253     public void onDestroy() {
254         super.onDestroy();
255         CatLog.d(LOG_TAG, "onDestroy - mIsResponseSent[" + mIsResponseSent +
256                 "], sim id: " + mSlotId);
257 
258         if (mAlertDialog != null && mAlertDialog.isShowing()) {
259             mAlertDialog.dismiss();
260             mAlertDialog = null;
261         }
262 
263         if (appService == null) {
264             return;
265         }
266         // if dialog activity is finished by stkappservice
267         // when receiving OP_LAUNCH_APP from the other SIM, we can not send TR here
268         // , since the dialog cmd is waiting user to process.
269         if (!isChangingConfigurations()) {
270             if (!mIsResponseSent && appService != null && !appService.isDialogPending(mSlotId)) {
271                 sendResponse(StkAppService.RES_ID_CONFIRM, false);
272             }
273             cancelTimeOut();
274         }
275         // Cleanup broadcast receivers to avoid leaks
276         if (mBroadcastReceiver != null) {
277             unregisterReceiver(mBroadcastReceiver);
278         }
279     }
280 
281     @Override
onSaveInstanceState(Bundle outState)282     public void onSaveInstanceState(Bundle outState) {
283         CatLog.d(LOG_TAG, "onSaveInstanceState");
284 
285         super.onSaveInstanceState(outState);
286 
287         outState.putParcelable(TEXT_KEY, mTextMsg);
288         outState.putParcelable(TIMEOUT_INTENT_KEY, mTimeoutIntent);
289         outState.putInt(SLOT_ID_KEY, mSlotId);
290     }
291 
292     @Override
onRestoreInstanceState(Bundle savedInstanceState)293     public void onRestoreInstanceState(Bundle savedInstanceState) {
294         super.onRestoreInstanceState(savedInstanceState);
295 
296         mTextMsg = savedInstanceState.getParcelable(TEXT_KEY);
297         mTimeoutIntent = savedInstanceState.getParcelable(TIMEOUT_INTENT_KEY);
298         mSlotId = savedInstanceState.getInt(SLOT_ID_KEY);
299         appService.getStkContext(mSlotId).setPendingDialogInstance(this);
300         CatLog.d(LOG_TAG, "onRestoreInstanceState - [" + mTextMsg + "]");
301     }
302 
303     @Override
onNewIntent(Intent intent)304     protected void onNewIntent(Intent intent) {
305         CatLog.d(LOG_TAG, "onNewIntent - updating the same Dialog box");
306         setIntent(intent);
307     }
308 
309     @Override
finish()310     public void finish() {
311         super.finish();
312         // Unregister the instance for DISPLAY TEXT command with an immediate response object
313         // as it is unnecessary to ask the service to finish this anymore.
314         if ((appService != null) && (mTextMsg != null) && !mTextMsg.responseNeeded) {
315             if (SubscriptionManager.isValidSlotIndex(mSlotId)) {
316                 appService.getStkContext(mSlotId).setImmediateDialogInstance(null);
317             }
318         }
319     }
320 
sendResponse(int resId, boolean confirmed)321     private void sendResponse(int resId, boolean confirmed) {
322         if (mSlotId == -1) {
323             CatLog.d(LOG_TAG, "sim id is invalid");
324             return;
325         }
326 
327         if (StkAppService.getInstance() == null) {
328             CatLog.d(LOG_TAG, "Ignore response: id is " + resId);
329             return;
330         }
331 
332         CatLog.d(LOG_TAG, "sendResponse resID[" + resId + "] confirmed[" + confirmed + "]");
333 
334         if (mTextMsg.responseNeeded) {
335             Bundle args = new Bundle();
336             args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
337             args.putInt(StkAppService.SLOT_ID, mSlotId);
338             args.putInt(StkAppService.RES_ID, resId);
339             args.putBoolean(StkAppService.CONFIRMATION, confirmed);
340             startService(new Intent(this, StkAppService.class).putExtras(args));
341             mIsResponseSent = true;
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         CatLog.d(LOG_TAG, "cancelTimeOut: " + mSlotId);
364         if (mTimeoutIntent != null) {
365             mAlarmManager.cancel(mTimeoutIntent);
366             mTimeoutIntent = null;
367         }
368     }
369 
startTimeOut(boolean waitForUserToClear)370     private void startTimeOut(boolean waitForUserToClear) {
371 
372         // Reset timeout.
373         cancelTimeOut();
374         int dialogDuration = StkApp.calculateDurationInMilis(mTextMsg.duration);
375         // If duration is specified, this has priority. If not, set timeout
376         // according to condition given by the card.
377         if (mTextMsg.userClear == true && mTextMsg.responseNeeded == false) {
378             return;
379         } else {
380             // userClear = false. will disappear after a while.
381             if (dialogDuration == 0) {
382                 if (waitForUserToClear) {
383                     dialogDuration = StkApp.DISP_TEXT_WAIT_FOR_USER_TIMEOUT;
384                 } else {
385                     dialogDuration = StkApp.DISP_TEXT_CLEAR_AFTER_DELAY_TIMEOUT;
386                 }
387             }
388             CatLog.d(LOG_TAG, "startTimeOut: " + mSlotId);
389             Intent mAlarmIntent = new Intent(ALARM_TIMEOUT);
390             mAlarmIntent.putExtra(StkAppService.SLOT_ID, mSlotId);
391             mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, mAlarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
392 
393             // Try to use a more stringent timer not affected by system sleep.
394             if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
395                 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
396                         SystemClock.elapsedRealtime() + dialogDuration, mTimeoutIntent);
397             }
398             else {
399                 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
400                         SystemClock.elapsedRealtime() + dialogDuration, mTimeoutIntent);
401             }
402         }
403     }
404 
405     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
406         @Override public void onReceive(Context context, Intent intent) {
407             String action = intent.getAction();
408             int slotID = intent.getIntExtra(StkAppService.SLOT_ID, 0);
409 
410             if (action == null || slotID != mSlotId) return;
411             CatLog.d(LOG_TAG, "onReceive, action=" + action + ", sim id: " + slotID);
412             if (action.equals(ALARM_TIMEOUT)) {
413                 CatLog.d(LOG_TAG, "ALARM_TIMEOUT rcvd");
414                 mTimeoutIntent = null;
415                 sendResponse(StkAppService.RES_ID_TIMEOUT);
416                 finish();
417             }
418         }
419     };
420 }
421