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