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