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