Home | History | Annotate | Download | only in cdma
      1 /*
      2  * Copyright (C) 2006 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.internal.telephony.cdma;
     18 
     19 import android.app.AlarmManager;
     20 import android.app.PendingIntent;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.os.AsyncResult;
     24 import android.os.Message;
     25 import android.os.SystemClock;
     26 import android.os.SystemProperties;
     27 import android.telephony.ServiceState;
     28 import android.telephony.TelephonyManager;
     29 import android.telephony.cdma.CdmaCellLocation;
     30 import android.text.TextUtils;
     31 import android.util.EventLog;
     32 import android.util.Log;
     33 
     34 import com.android.internal.telephony.ApnSetting;
     35 import com.android.internal.telephony.CommandsInterface;
     36 import com.android.internal.telephony.DataCallState;
     37 import com.android.internal.telephony.DataConnection.FailCause;
     38 import com.android.internal.telephony.DataConnection;
     39 import com.android.internal.telephony.DataConnectionAc;
     40 import com.android.internal.telephony.DataConnectionTracker;
     41 import com.android.internal.telephony.DctConstants;
     42 import com.android.internal.telephony.EventLogTags;
     43 import com.android.internal.telephony.PhoneConstants;
     44 import com.android.internal.telephony.IccRecords;
     45 import com.android.internal.telephony.Phone;
     46 import com.android.internal.telephony.RetryManager;
     47 import com.android.internal.telephony.RILConstants;
     48 import com.android.internal.telephony.UiccCard;
     49 import com.android.internal.telephony.uicc.UiccController;
     50 import com.android.internal.util.AsyncChannel;
     51 
     52 import java.io.FileDescriptor;
     53 import java.io.PrintWriter;
     54 import java.util.ArrayList;
     55 
     56 /**
     57  * {@hide}
     58  */
     59 public final class CdmaDataConnectionTracker extends DataConnectionTracker {
     60     protected final String LOG_TAG = "CDMA";
     61 
     62     private CDMAPhone mCdmaPhone;
     63     private CdmaSubscriptionSourceManager mCdmaSSM;
     64 
     65     /** The DataConnection being setup */
     66     private CdmaDataConnection mPendingDataConnection;
     67 
     68     private boolean mPendingRestartRadio = false;
     69     private static final int TIME_DELAYED_TO_RESTART_RADIO =
     70             SystemProperties.getInt("ro.cdma.timetoradiorestart", 60000);
     71 
     72     /**
     73      * Pool size of CdmaDataConnection objects.
     74      */
     75     private static final int DATA_CONNECTION_POOL_SIZE = 1;
     76 
     77     private static final String INTENT_RECONNECT_ALARM =
     78         "com.android.internal.telephony.cdma-reconnect";
     79 
     80     private static final String INTENT_DATA_STALL_ALARM =
     81         "com.android.internal.telephony.cdma-data-stall";
     82 
     83     private static final String[] mSupportedApnTypes = {
     84             PhoneConstants.APN_TYPE_DEFAULT,
     85             PhoneConstants.APN_TYPE_MMS,
     86             PhoneConstants.APN_TYPE_DUN,
     87             PhoneConstants.APN_TYPE_HIPRI };
     88 
     89     private static final String[] mDefaultApnTypes = {
     90             PhoneConstants.APN_TYPE_DEFAULT,
     91             PhoneConstants.APN_TYPE_MMS,
     92             PhoneConstants.APN_TYPE_HIPRI };
     93 
     94     private String[] mDunApnTypes = {
     95             PhoneConstants.APN_TYPE_DUN };
     96 
     97     private static final int mDefaultApnId = DctConstants.APN_DEFAULT_ID;
     98 
     99     /* Constructor */
    100 
    101     CdmaDataConnectionTracker(CDMAPhone p) {
    102         super(p);
    103         mCdmaPhone = p;
    104 
    105         p.mCM.registerForAvailable (this, DctConstants.EVENT_RADIO_AVAILABLE, null);
    106         p.mCM.registerForOffOrNotAvailable(this, DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
    107         p.mCM.registerForDataNetworkStateChanged (this, DctConstants.EVENT_DATA_STATE_CHANGED, null);
    108         p.mCT.registerForVoiceCallEnded (this, DctConstants.EVENT_VOICE_CALL_ENDED, null);
    109         p.mCT.registerForVoiceCallStarted (this, DctConstants.EVENT_VOICE_CALL_STARTED, null);
    110         p.mSST.registerForDataConnectionAttached(this, DctConstants.EVENT_TRY_SETUP_DATA, null);
    111         p.mSST.registerForDataConnectionDetached(this, DctConstants.EVENT_CDMA_DATA_DETACHED, null);
    112         p.mSST.registerForRoamingOn(this, DctConstants.EVENT_ROAMING_ON, null);
    113         p.mSST.registerForRoamingOff(this, DctConstants.EVENT_ROAMING_OFF, null);
    114         p.mCM.registerForCdmaOtaProvision(this, DctConstants.EVENT_CDMA_OTA_PROVISION, null);
    115         mCdmaSSM = CdmaSubscriptionSourceManager.getInstance (p.getContext(), p.mCM, this,
    116                 DctConstants.EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
    117 
    118         mDataConnectionTracker = this;
    119 
    120         createAllDataConnectionList();
    121         broadcastMessenger();
    122 
    123         Context c = mCdmaPhone.getContext();
    124         String[] t = c.getResources().getStringArray(
    125                 com.android.internal.R.array.config_cdma_dun_supported_types);
    126         if (t != null && t.length > 0) {
    127             ArrayList<String> temp = new ArrayList<String>();
    128             for(int i=0; i< t.length; i++) {
    129                 if (!PhoneConstants.APN_TYPE_DUN.equalsIgnoreCase(t[i])) {
    130                     temp.add(t[i]);
    131                 }
    132             }
    133             temp.add(0,PhoneConstants.APN_TYPE_DUN);
    134             mDunApnTypes = temp.toArray(t);
    135         }
    136 
    137     }
    138 
    139     @Override
    140     public void dispose() {
    141         cleanUpConnection(true, null, false);
    142 
    143         super.dispose();
    144 
    145         // Unregister from all events
    146         mPhone.mCM.unregisterForAvailable(this);
    147         mPhone.mCM.unregisterForOffOrNotAvailable(this);
    148         IccRecords r = mIccRecords.get();
    149         if (r != null) { r.unregisterForRecordsLoaded(this);}
    150         mPhone.mCM.unregisterForDataNetworkStateChanged(this);
    151         mCdmaPhone.mCT.unregisterForVoiceCallEnded(this);
    152         mCdmaPhone.mCT.unregisterForVoiceCallStarted(this);
    153         mCdmaPhone.mSST.unregisterForDataConnectionAttached(this);
    154         mCdmaPhone.mSST.unregisterForDataConnectionDetached(this);
    155         mCdmaPhone.mSST.unregisterForRoamingOn(this);
    156         mCdmaPhone.mSST.unregisterForRoamingOff(this);
    157         mCdmaSSM.dispose(this);
    158         mPhone.mCM.unregisterForCdmaOtaProvision(this);
    159 
    160         destroyAllDataConnectionList();
    161     }
    162 
    163     @Override
    164     protected void finalize() {
    165         if(DBG) log("CdmaDataConnectionTracker finalized");
    166     }
    167 
    168     @Override
    169     protected String getActionIntentReconnectAlarm() {
    170         return INTENT_RECONNECT_ALARM;
    171     }
    172 
    173     @Override
    174     protected String getActionIntentDataStallAlarm() {
    175         return INTENT_DATA_STALL_ALARM;
    176     }
    177 
    178     @Override
    179     protected void restartDataStallAlarm() {}
    180 
    181     @Override
    182     protected void setState(DctConstants.State s) {
    183         if (DBG) log ("setState: " + s);
    184         if (mState != s) {
    185             EventLog.writeEvent(EventLogTags.CDMA_DATA_STATE_CHANGE,
    186                     mState.toString(), s.toString());
    187             mState = s;
    188         }
    189     }
    190 
    191     @Override
    192     public synchronized DctConstants.State getState(String apnType) {
    193         return mState;
    194     }
    195 
    196     @Override
    197     public DctConstants.State getOverallState() {
    198         return mState;
    199     }
    200 
    201     @Override
    202     protected boolean isApnTypeAvailable(String type) {
    203         for (String s : mSupportedApnTypes) {
    204             if (TextUtils.equals(type, s)) {
    205                 return true;
    206             }
    207         }
    208         return false;
    209     }
    210 
    211     @Override
    212     protected boolean isDataAllowed() {
    213         final boolean internalDataEnabled;
    214         synchronized (mDataEnabledLock) {
    215             internalDataEnabled = mInternalDataEnabled;
    216         }
    217 
    218         int psState = mCdmaPhone.mSST.getCurrentDataConnectionState();
    219         boolean roaming = (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled());
    220         boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
    221         boolean subscriptionFromNv = (mCdmaSSM.getCdmaSubscriptionSource()
    222                                        == CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_NV);
    223 
    224         IccRecords r = mIccRecords.get();
    225         boolean allowed =
    226                     (psState == ServiceState.STATE_IN_SERVICE ||
    227                             mAutoAttachOnCreation) &&
    228                     (subscriptionFromNv ||
    229                             (r != null && r.getRecordsLoaded())) &&
    230                     (mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() ||
    231                             mPhone.getState() ==PhoneConstants.State.IDLE) &&
    232                     !roaming &&
    233                     internalDataEnabled &&
    234                     desiredPowerState &&
    235                     !mPendingRestartRadio &&
    236                     ((mPhone.getLteOnCdmaMode() ==PhoneConstants.LTE_ON_CDMA_TRUE) ||
    237                             !mCdmaPhone.needsOtaServiceProvisioning());
    238         if (!allowed && DBG) {
    239             String reason = "";
    240             if (!((psState == ServiceState.STATE_IN_SERVICE) || mAutoAttachOnCreation)) {
    241                 reason += " - psState= " + psState;
    242             }
    243             if (!subscriptionFromNv &&
    244                     !(r != null && r.getRecordsLoaded())) {
    245                 reason += " - RUIM not loaded";
    246             }
    247             if (!(mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() ||
    248                     mPhone.getState() ==PhoneConstants.State.IDLE)) {
    249                 reason += " - concurrentVoiceAndData not allowed and state= " + mPhone.getState();
    250             }
    251             if (roaming) reason += " - Roaming";
    252             if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
    253             if (!desiredPowerState) reason += " - desiredPowerState= false";
    254             if (mPendingRestartRadio) reason += " - mPendingRestartRadio= true";
    255             if (mCdmaPhone.needsOtaServiceProvisioning()) reason += " - needs Provisioning";
    256             log("Data not allowed due to" + reason);
    257         }
    258         return allowed;
    259     }
    260 
    261     @Override
    262     protected boolean isDataPossible(String apnType) {
    263         boolean possible = isDataAllowed() && !(getAnyDataEnabled() &&
    264                 mState == DctConstants.State.FAILED);
    265         if (!possible && DBG && isDataAllowed()) {
    266             log("Data not possible.  No coverage: dataState = " + mState);
    267         }
    268         return possible;
    269     }
    270 
    271     private boolean trySetupData(String reason) {
    272         if (DBG) log("***trySetupData due to " + (reason == null ? "(unspecified)" : reason));
    273 
    274         if (mPhone.getSimulatedRadioControl() != null) {
    275             // Assume data is connected on the simulator
    276             // FIXME  this can be improved
    277             setState(DctConstants.State.CONNECTED);
    278             notifyDataConnection(reason);
    279             notifyOffApnsOfAvailability(reason);
    280 
    281             log("(fix?) We're on the simulator; assuming data is connected");
    282             return true;
    283         }
    284 
    285         int psState = mCdmaPhone.mSST.getCurrentDataConnectionState();
    286         boolean roaming = mPhone.getServiceState().getRoaming();
    287         boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
    288 
    289         if ((mState == DctConstants.State.IDLE || mState == DctConstants.State.SCANNING) &&
    290                 isDataAllowed() && getAnyDataEnabled() && !isEmergency()) {
    291             boolean retValue = setupData(reason);
    292             notifyOffApnsOfAvailability(reason);
    293             return retValue;
    294         } else {
    295             notifyOffApnsOfAvailability(reason);
    296             return false;
    297         }
    298     }
    299 
    300     /**
    301      * Cleanup the CDMA data connection (only one is supported)
    302      *
    303      * @param tearDown true if the underlying DataConnection should be disconnected.
    304      * @param reason for the clean up.
    305      * @param doAll Set RefCount to 0 and tear down data call even if
    306      *              multiple APN types are associated with it.
    307      */
    308     private void cleanUpConnection(boolean tearDown, String reason, boolean doAll) {
    309         if (DBG) log("cleanUpConnection: reason: " + reason);
    310 
    311         // Clear the reconnect alarm, if set.
    312         if (mReconnectIntent != null) {
    313             AlarmManager am =
    314                 (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
    315             am.cancel(mReconnectIntent);
    316             mReconnectIntent = null;
    317         }
    318 
    319         setState(DctConstants.State.DISCONNECTING);
    320         notifyOffApnsOfAvailability(reason);
    321 
    322         boolean notificationDeferred = false;
    323         for (DataConnection conn : mDataConnections.values()) {
    324             if(conn != null) {
    325                 DataConnectionAc dcac =
    326                     mDataConnectionAsyncChannels.get(conn.getDataConnectionId());
    327                 if (tearDown) {
    328                     if (doAll) {
    329                         if (DBG) log("cleanUpConnection: teardown, conn.tearDownAll");
    330                         conn.tearDownAll(reason, obtainMessage(DctConstants.EVENT_DISCONNECT_DONE,
    331                                 conn.getDataConnectionId(), 0, reason));
    332                     } else {
    333                         if (DBG) log("cleanUpConnection: teardown, conn.tearDown");
    334                         conn.tearDown(reason, obtainMessage(DctConstants.EVENT_DISCONNECT_DONE,
    335                                 conn.getDataConnectionId(), 0, reason));
    336                     }
    337                     notificationDeferred = true;
    338                 } else {
    339                     if (DBG) log("cleanUpConnection: !tearDown, call conn.resetSynchronously");
    340                     if (dcac != null) {
    341                         dcac.resetSync();
    342                     }
    343                     notificationDeferred = false;
    344                 }
    345             }
    346         }
    347 
    348         stopNetStatPoll();
    349         stopDataStallAlarm();
    350 
    351         if (!notificationDeferred) {
    352             if (DBG) log("cleanupConnection: !notificationDeferred");
    353             gotoIdleAndNotifyDataConnection(reason);
    354         }
    355     }
    356 
    357     private CdmaDataConnection findFreeDataConnection() {
    358         for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
    359             if (dcac.isInactiveSync()) {
    360                 log("found free GsmDataConnection");
    361                 return (CdmaDataConnection) dcac.dataConnection;
    362             }
    363         }
    364         log("NO free CdmaDataConnection");
    365         return null;
    366     }
    367 
    368     private boolean setupData(String reason) {
    369         CdmaDataConnection conn = findFreeDataConnection();
    370 
    371         if (conn == null) {
    372             if (DBG) log("setupData: No free CdmaDataConnection found!");
    373             return false;
    374         }
    375 
    376         /** TODO: We probably want the connection being setup to a parameter passed around */
    377         mPendingDataConnection = conn;
    378         String[] types;
    379         int apnId;
    380         if (mRequestedApnType.equals(PhoneConstants.APN_TYPE_DUN)) {
    381             types = mDunApnTypes;
    382             apnId = DctConstants.APN_DUN_ID;
    383         } else {
    384             types = mDefaultApnTypes;
    385             apnId = mDefaultApnId;
    386         }
    387         mActiveApn = new ApnSetting(apnId, "", "", "", "", "", "", "", "", "",
    388                                     "", 0, types, "IP", "IP", true, 0);
    389         if (DBG) log("call conn.bringUp mActiveApn=" + mActiveApn);
    390 
    391         Message msg = obtainMessage();
    392         msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
    393         msg.obj = reason;
    394         conn.bringUp(msg, mActiveApn);
    395 
    396         setState(DctConstants.State.INITING);
    397         notifyDataConnection(reason);
    398         return true;
    399     }
    400 
    401     private void notifyDefaultData(String reason) {
    402         setState(DctConstants.State.CONNECTED);
    403         notifyDataConnection(reason);
    404         startNetStatPoll();
    405         startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
    406         mDataConnections.get(0).resetRetryCount();
    407     }
    408 
    409     @Override
    410     protected void restartRadio() {
    411         if (DBG) log("Cleanup connection and wait " +
    412                 (TIME_DELAYED_TO_RESTART_RADIO / 1000) + "s to restart radio");
    413         cleanUpAllConnections(null);
    414         sendEmptyMessageDelayed(DctConstants.EVENT_RESTART_RADIO, TIME_DELAYED_TO_RESTART_RADIO);
    415         mPendingRestartRadio = true;
    416     }
    417 
    418     /**
    419      * Returns true if the last fail cause is something that
    420      * seems like it deserves an error notification.
    421      * Transient errors are ignored
    422      */
    423     private boolean
    424     shouldPostNotification(FailCause cause) {
    425         return (cause != FailCause.UNKNOWN);
    426     }
    427 
    428     /**
    429      * Return true if data connection need to be setup after disconnected due to
    430      * reason.
    431      *
    432      * @param reason the reason why data is disconnected
    433      * @return true if try setup data connection is need for this reason
    434      */
    435     private boolean retryAfterDisconnected(String reason) {
    436         boolean retry = true;
    437 
    438         if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
    439             retry = false;
    440         }
    441         return retry;
    442     }
    443 
    444     private void reconnectAfterFail(FailCause lastFailCauseCode, String reason, int retryOverride) {
    445         if (mState == DctConstants.State.FAILED) {
    446             /**
    447              * For now With CDMA we never try to reconnect on
    448              * error and instead just continue to retry
    449              * at the last time until the state is changed.
    450              * TODO: Make this configurable?
    451              */
    452             int nextReconnectDelay = retryOverride;
    453             if (nextReconnectDelay < 0) {
    454                 nextReconnectDelay = mDataConnections.get(0).getRetryTimer();
    455                 mDataConnections.get(0).increaseRetryCount();
    456             }
    457             startAlarmForReconnect(nextReconnectDelay, reason);
    458 
    459             if (!shouldPostNotification(lastFailCauseCode)) {
    460                 log("NOT Posting Data Connection Unavailable notification "
    461                                 + "-- likely transient error");
    462             } else {
    463                 notifyNoData(lastFailCauseCode);
    464             }
    465         }
    466     }
    467 
    468     private void startAlarmForReconnect(int delay, String reason) {
    469 
    470         log("Data Connection activate failed. Scheduling next attempt for "
    471                 + (delay / 1000) + "s");
    472 
    473         AlarmManager am =
    474             (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
    475         Intent intent = new Intent(INTENT_RECONNECT_ALARM);
    476         intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
    477         mReconnectIntent = PendingIntent.getBroadcast(
    478                 mPhone.getContext(), 0, intent, 0);
    479         am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    480                 SystemClock.elapsedRealtime() + delay, mReconnectIntent);
    481 
    482     }
    483 
    484     private void notifyNoData(FailCause lastFailCauseCode) {
    485         setState(DctConstants.State.FAILED);
    486         notifyOffApnsOfAvailability(null);
    487     }
    488 
    489     protected void gotoIdleAndNotifyDataConnection(String reason) {
    490         if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
    491         setState(DctConstants.State.IDLE);
    492         notifyDataConnection(reason);
    493         mActiveApn = null;
    494     }
    495 
    496     protected void onRecordsLoaded() {
    497         if (mState == DctConstants.State.FAILED) {
    498             cleanUpAllConnections(null);
    499         }
    500         sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, Phone.REASON_SIM_LOADED));
    501     }
    502 
    503     protected void onNVReady() {
    504         if (mState == DctConstants.State.FAILED) {
    505             cleanUpAllConnections(null);
    506         }
    507         sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA));
    508     }
    509 
    510     /**
    511      * @override com.android.internal.telephony.DataConnectionTracker
    512      */
    513     @Override
    514     protected void onEnableNewApn() {
    515         // No mRequestedApnType check; only one connection is supported
    516         cleanUpConnection(true, Phone.REASON_APN_SWITCHED, false);
    517     }
    518 
    519     /**
    520      * @override com.android.internal.telephony.DataConnectionTracker
    521      */
    522     @Override
    523     protected boolean onTrySetupData(String reason) {
    524         return trySetupData(reason);
    525     }
    526 
    527     /**
    528      * @override com.android.internal.telephony.DataConnectionTracker
    529      */
    530     @Override
    531     protected void onRoamingOff() {
    532         if (mUserDataEnabled == false) return;
    533 
    534         if (getDataOnRoamingEnabled() == false) {
    535             notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF);
    536             trySetupData(Phone.REASON_ROAMING_OFF);
    537         } else {
    538             notifyDataConnection(Phone.REASON_ROAMING_OFF);
    539         }
    540     }
    541 
    542     /**
    543      * @override com.android.internal.telephony.DataConnectionTracker
    544      */
    545     @Override
    546     protected void onRoamingOn() {
    547         if (mUserDataEnabled == false) return;
    548 
    549         if (getDataOnRoamingEnabled()) {
    550             trySetupData(Phone.REASON_ROAMING_ON);
    551             notifyDataConnection(Phone.REASON_ROAMING_ON);
    552         } else {
    553             if (DBG) log("Tear down data connection on roaming.");
    554             cleanUpAllConnections(null);
    555             notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
    556         }
    557     }
    558 
    559     /**
    560      * @override com.android.internal.telephony.DataConnectionTracker
    561      */
    562     @Override
    563     protected void onRadioAvailable() {
    564         if (mPhone.getSimulatedRadioControl() != null) {
    565             // Assume data is connected on the simulator
    566             // FIXME  this can be improved
    567             setState(DctConstants.State.CONNECTED);
    568             notifyDataConnection(null);
    569 
    570             log("We're on the simulator; assuming data is connected");
    571         }
    572 
    573         notifyOffApnsOfAvailability(null);
    574 
    575         if (mState != DctConstants.State.IDLE) {
    576             cleanUpAllConnections(null);
    577         }
    578     }
    579 
    580     /**
    581      * @override com.android.internal.telephony.DataConnectionTracker
    582      */
    583     @Override
    584     protected void onRadioOffOrNotAvailable() {
    585         mDataConnections.get(0).resetRetryCount();
    586 
    587         if (mPhone.getSimulatedRadioControl() != null) {
    588             // Assume data is connected on the simulator
    589             // FIXME  this can be improved
    590             log("We're on the simulator; assuming radio off is meaningless");
    591         } else {
    592             if (DBG) log("Radio is off and clean up all connection");
    593             cleanUpAllConnections(null);
    594         }
    595     }
    596 
    597     /**
    598      * @override com.android.internal.telephony.DataConnectionTracker
    599      */
    600     @Override
    601     protected void onDataSetupComplete(AsyncResult ar) {
    602         String reason = null;
    603         if (ar.userObj instanceof String) {
    604             reason = (String) ar.userObj;
    605         }
    606 
    607         if (isDataSetupCompleteOk(ar)) {
    608             // Everything is setup
    609             notifyDefaultData(reason);
    610         } else {
    611             FailCause cause = (FailCause) (ar.result);
    612             if(DBG) log("Data Connection setup failed " + cause);
    613 
    614             // No try for permanent failure
    615             if (cause.isPermanentFail()) {
    616                 notifyNoData(cause);
    617                 return;
    618             }
    619 
    620             int retryOverride = -1;
    621             if (ar.exception instanceof DataConnection.CallSetupException) {
    622                 retryOverride =
    623                     ((DataConnection.CallSetupException)ar.exception).getRetryOverride();
    624             }
    625             if (retryOverride == RILConstants.MAX_INT) {
    626                 if (DBG) log("No retry is suggested.");
    627             } else {
    628                 startDelayedRetry(cause, reason, retryOverride);
    629             }
    630         }
    631     }
    632 
    633     /**
    634      * Called when DctConstants.EVENT_DISCONNECT_DONE is received.
    635      */
    636     @Override
    637     protected void onDisconnectDone(int connId, AsyncResult ar) {
    638         if(DBG) log("EVENT_DISCONNECT_DONE connId=" + connId);
    639         String reason = null;
    640         if (ar.userObj instanceof String) {
    641             reason = (String) ar.userObj;
    642         }
    643         setState(DctConstants.State.IDLE);
    644 
    645         // Since the pending request to turn off or restart radio will be processed here,
    646         // remove the pending event to restart radio from the message queue.
    647         if (mPendingRestartRadio) removeMessages(DctConstants.EVENT_RESTART_RADIO);
    648 
    649         // Process the pending request to turn off radio in ServiceStateTracker first.
    650         // If radio is turned off in ServiceStateTracker, ignore the pending event to restart radio.
    651         CdmaServiceStateTracker ssTracker = mCdmaPhone.mSST;
    652         if (ssTracker.processPendingRadioPowerOffAfterDataOff()) {
    653             mPendingRestartRadio = false;
    654         } else {
    655             onRestartRadio();
    656         }
    657 
    658         notifyDataConnection(reason);
    659         mActiveApn = null;
    660         if (retryAfterDisconnected(reason)) {
    661           // Wait a bit before trying, so we're not tying up RIL command channel.
    662           startAlarmForReconnect(APN_DELAY_MILLIS, reason);
    663       }
    664     }
    665 
    666     /**
    667      * @override com.android.internal.telephony.DataConnectionTracker
    668      */
    669     @Override
    670     protected void onVoiceCallStarted() {
    671         if (mState == DctConstants.State.CONNECTED &&
    672                 !mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed()) {
    673             stopNetStatPoll();
    674             stopDataStallAlarm();
    675             notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
    676             notifyOffApnsOfAvailability(Phone.REASON_VOICE_CALL_STARTED);
    677         }
    678     }
    679 
    680     /**
    681      * @override com.android.internal.telephony.DataConnectionTracker
    682      */
    683     @Override
    684     protected void onVoiceCallEnded() {
    685         if (mState == DctConstants.State.CONNECTED) {
    686             if (!mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed()) {
    687                 startNetStatPoll();
    688                 startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
    689                 notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
    690             } else {
    691                 // clean slate after call end.
    692                 resetPollStats();
    693             }
    694             notifyOffApnsOfAvailability(Phone.REASON_VOICE_CALL_ENDED);
    695         } else {
    696             mDataConnections.get(0).resetRetryCount();
    697             // in case data setup was attempted when we were on a voice call
    698             trySetupData(Phone.REASON_VOICE_CALL_ENDED);
    699         }
    700     }
    701 
    702     @Override
    703     protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
    704         // No apnId check; only one connection is supported
    705         cleanUpConnection(tearDown, reason, (apnId == DctConstants.APN_DUN_ID));
    706     }
    707 
    708     @Override
    709     protected void onCleanUpAllConnections(String cause) {
    710         // Only one CDMA connection is supported
    711         cleanUpConnection(true, cause, false);
    712     }
    713 
    714     private void createAllDataConnectionList() {
    715         CdmaDataConnection dataConn;
    716 
    717         String retryConfig = SystemProperties.get("ro.cdma.data_retry_config");
    718         for (int i = 0; i < DATA_CONNECTION_POOL_SIZE; i++) {
    719             RetryManager rm = new RetryManager();
    720             if (!rm.configure(retryConfig)) {
    721                 if (!rm.configure(DEFAULT_DATA_RETRY_CONFIG)) {
    722                     // Should never happen, log an error and default to a simple linear sequence.
    723                     log("Could not configure using DEFAULT_DATA_RETRY_CONFIG="
    724                             + DEFAULT_DATA_RETRY_CONFIG);
    725                     rm.configure(20, 2000, 1000);
    726                 }
    727             }
    728 
    729             int id = mUniqueIdGenerator.getAndIncrement();
    730             dataConn = CdmaDataConnection.makeDataConnection(mCdmaPhone, id, rm, this);
    731             mDataConnections.put(id, dataConn);
    732             DataConnectionAc dcac = new DataConnectionAc(dataConn, LOG_TAG);
    733             int status = dcac.fullyConnectSync(mPhone.getContext(), this, dataConn.getHandler());
    734             if (status == AsyncChannel.STATUS_SUCCESSFUL) {
    735                 log("Fully connected");
    736                 mDataConnectionAsyncChannels.put(dcac.dataConnection.getDataConnectionId(), dcac);
    737             } else {
    738                 log("Could not connect to dcac.dataConnection=" + dcac.dataConnection +
    739                         " status=" + status);
    740             }
    741 
    742         }
    743     }
    744 
    745     private void destroyAllDataConnectionList() {
    746         if(mDataConnections != null) {
    747             mDataConnections.clear();
    748         }
    749     }
    750 
    751     private void onCdmaDataDetached() {
    752         if (mState == DctConstants.State.CONNECTED) {
    753             startNetStatPoll();
    754             startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
    755             notifyDataConnection(Phone.REASON_CDMA_DATA_DETACHED);
    756         } else {
    757             if (mState == DctConstants.State.FAILED) {
    758                 cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED, false);
    759                 mDataConnections.get(0).resetRetryCount();
    760 
    761                 CdmaCellLocation loc = (CdmaCellLocation)(mPhone.getCellLocation());
    762                 EventLog.writeEvent(EventLogTags.CDMA_DATA_SETUP_FAILED,
    763                         loc != null ? loc.getBaseStationId() : -1,
    764                         TelephonyManager.getDefault().getNetworkType());
    765             }
    766             trySetupData(Phone.REASON_CDMA_DATA_DETACHED);
    767         }
    768     }
    769 
    770     private void onCdmaOtaProvision(AsyncResult ar) {
    771         if (ar.exception != null) {
    772             int [] otaPrivision = (int [])ar.result;
    773             if ((otaPrivision != null) && (otaPrivision.length > 1)) {
    774                 switch (otaPrivision[0]) {
    775                 case Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED:
    776                 case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED:
    777                     mDataConnections.get(0).resetRetryCount();
    778                     break;
    779                 default:
    780                     break;
    781                 }
    782             }
    783         }
    784     }
    785 
    786     private void onRestartRadio() {
    787         if (mPendingRestartRadio) {
    788             log("************TURN OFF RADIO**************");
    789             mPhone.mCM.setRadioPower(false, null);
    790             /* Note: no need to call setRadioPower(true).  Assuming the desired
    791              * radio power state is still ON (as tracked by ServiceStateTracker),
    792              * ServiceStateTracker will call setRadioPower when it receives the
    793              * RADIO_STATE_CHANGED notification for the power off.  And if the
    794              * desired power state has changed in the interim, we don't want to
    795              * override it with an unconditional power on.
    796              */
    797             mPendingRestartRadio = false;
    798         }
    799     }
    800 
    801     private void writeEventLogCdmaDataDrop() {
    802         CdmaCellLocation loc = (CdmaCellLocation)(mPhone.getCellLocation());
    803         EventLog.writeEvent(EventLogTags.CDMA_DATA_DROP,
    804                 loc != null ? loc.getBaseStationId() : -1,
    805                 TelephonyManager.getDefault().getNetworkType());
    806     }
    807 
    808     protected void onDataStateChanged(AsyncResult ar) {
    809         ArrayList<DataCallState> dataCallStates = (ArrayList<DataCallState>)(ar.result);
    810 
    811         if (ar.exception != null) {
    812             // This is probably "radio not available" or something
    813             // of that sort. If so, the whole connection is going
    814             // to come down soon anyway
    815             return;
    816         }
    817 
    818         if (mState == DctConstants.State.CONNECTED) {
    819             boolean isActiveOrDormantConnectionPresent = false;
    820             int connectionState = DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE;
    821 
    822             // Check for an active or dormant connection element in
    823             // the DATA_CALL_LIST array
    824             for (int index = 0; index < dataCallStates.size(); index++) {
    825                 connectionState = dataCallStates.get(index).active;
    826                 if (connectionState != DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) {
    827                     isActiveOrDormantConnectionPresent = true;
    828                     break;
    829                 }
    830             }
    831 
    832             if (!isActiveOrDormantConnectionPresent) {
    833                 // No active or dormant connection
    834                 log("onDataStateChanged: No active connection"
    835                         + "state is CONNECTED, disconnecting/cleanup");
    836                 writeEventLogCdmaDataDrop();
    837                 cleanUpConnection(true, null, false);
    838                 return;
    839             }
    840 
    841             switch (connectionState) {
    842                 case DATA_CONNECTION_ACTIVE_PH_LINK_UP:
    843                     log("onDataStateChanged: active=LINK_ACTIVE && CONNECTED, ignore");
    844                     mActivity = DctConstants.Activity.NONE;
    845                     mPhone.notifyDataActivity();
    846                     startNetStatPoll();
    847                     startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
    848                     break;
    849 
    850                 case DATA_CONNECTION_ACTIVE_PH_LINK_DOWN:
    851                     log("onDataStateChanged active=LINK_DOWN && CONNECTED, dormant");
    852                     mActivity = DctConstants.Activity.DORMANT;
    853                     mPhone.notifyDataActivity();
    854                     stopNetStatPoll();
    855                     stopDataStallAlarm();
    856                     break;
    857 
    858                 default:
    859                     log("onDataStateChanged: IGNORE unexpected DataCallState.active="
    860                             + connectionState);
    861             }
    862         } else {
    863             // TODO: Do we need to do anything?
    864             log("onDataStateChanged: not connected, state=" + mState + " ignoring");
    865         }
    866     }
    867 
    868     private void startDelayedRetry(FailCause cause, String reason, int retryOverride) {
    869         notifyNoData(cause);
    870         reconnectAfterFail(cause, reason, retryOverride);
    871     }
    872 
    873     @Override
    874     public void handleMessage (Message msg) {
    875         if (DBG) log("CdmaDCT handleMessage msg=" + msg);
    876 
    877         if (!mPhone.mIsTheCurrentActivePhone || mIsDisposed) {
    878             log("Ignore CDMA msgs since CDMA phone is inactive");
    879             return;
    880         }
    881 
    882         switch (msg.what) {
    883             case DctConstants.EVENT_RECORDS_LOADED:
    884                 onRecordsLoaded();
    885                 break;
    886 
    887             case DctConstants.EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
    888                 if(mCdmaSSM.getCdmaSubscriptionSource() ==
    889                        CdmaSubscriptionSourceManager.SUBSCRIPTION_FROM_NV) {
    890                     onNVReady();
    891                 }
    892                 break;
    893 
    894             case DctConstants.EVENT_CDMA_DATA_DETACHED:
    895                 onCdmaDataDetached();
    896                 break;
    897 
    898             case DctConstants.EVENT_DATA_STATE_CHANGED:
    899                 onDataStateChanged((AsyncResult) msg.obj);
    900                 break;
    901 
    902             case DctConstants.EVENT_CDMA_OTA_PROVISION:
    903                 onCdmaOtaProvision((AsyncResult) msg.obj);
    904                 break;
    905 
    906             case DctConstants.EVENT_RESTART_RADIO:
    907                 if (DBG) log("EVENT_RESTART_RADIO");
    908                 onRestartRadio();
    909                 break;
    910 
    911             default:
    912                 // handle the message in the super class DataConnectionTracker
    913                 super.handleMessage(msg);
    914                 break;
    915         }
    916     }
    917 
    918     @Override
    919     protected void onUpdateIcc() {
    920         if (mUiccController == null ) {
    921             return;
    922         }
    923 
    924         IccRecords newIccRecords = mUiccController.getIccRecords(UiccController.APP_FAM_3GPP2);
    925 
    926         IccRecords r = mIccRecords.get();
    927         if (r != newIccRecords) {
    928             if (r != null) {
    929                 log("Removing stale icc objects.");
    930                 r.unregisterForRecordsLoaded(this);
    931                 mIccRecords.set(null);
    932             }
    933             if (newIccRecords != null) {
    934                 log("New records found");
    935                 mIccRecords.set(newIccRecords);
    936                 newIccRecords.registerForRecordsLoaded(
    937                         this, DctConstants.EVENT_RECORDS_LOADED, null);
    938             }
    939         }
    940     }
    941 
    942     @Override
    943     public boolean isDisconnected() {
    944         return ((mState == DctConstants.State.IDLE) || (mState == DctConstants.State.FAILED));
    945     }
    946 
    947     @Override
    948     protected boolean isConnected() {
    949         return (mState == DctConstants.State.CONNECTED);
    950     }
    951 
    952     @Override
    953     protected void log(String s) {
    954         Log.d(LOG_TAG, "[CdmaDCT] " + s);
    955     }
    956 
    957     @Override
    958     protected void loge(String s) {
    959         Log.e(LOG_TAG, "[CdmaDCT] " + s);
    960     }
    961 
    962     @Override
    963     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    964         pw.println("CdmaDataConnectionTracker extends:");
    965         super.dump(fd, pw, args);
    966         pw.println(" mCdmaPhone=" + mCdmaPhone);
    967         pw.println(" mCdmaSSM=" + mCdmaSSM);
    968         pw.println(" mPendingDataConnection=" + mPendingDataConnection);
    969         pw.println(" mPendingRestartRadio=" + mPendingRestartRadio);
    970         pw.println(" mSupportedApnTypes=" + mSupportedApnTypes);
    971         pw.println(" mDefaultApnTypes=" + mDefaultApnTypes);
    972         pw.println(" mDunApnTypes=" + mDunApnTypes);
    973         pw.println(" mDefaultApnId=" + mDefaultApnId);
    974     }
    975 }
    976