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