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