Home | History | Annotate | Download | only in dataconnection
      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.dataconnection;
     18 
     19 
     20 import com.android.internal.telephony.CommandException;
     21 import com.android.internal.telephony.DctConstants;
     22 import com.android.internal.telephony.Phone;
     23 import com.android.internal.telephony.PhoneBase;
     24 import com.android.internal.telephony.PhoneConstants;
     25 import com.android.internal.telephony.RILConstants;
     26 import com.android.internal.telephony.RetryManager;
     27 import com.android.internal.util.AsyncChannel;
     28 import com.android.internal.util.Protocol;
     29 import com.android.internal.util.State;
     30 import com.android.internal.util.StateMachine;
     31 
     32 import android.app.PendingIntent;
     33 import android.net.LinkCapabilities;
     34 import android.net.LinkProperties;
     35 import android.net.ProxyProperties;
     36 import android.os.AsyncResult;
     37 import android.os.Build;
     38 import android.os.Message;
     39 import android.os.SystemClock;
     40 import android.os.SystemProperties;
     41 import android.text.TextUtils;
     42 import android.telephony.Rlog;
     43 import android.telephony.TelephonyManager;
     44 import android.util.Patterns;
     45 import android.util.TimeUtils;
     46 
     47 import java.io.FileDescriptor;
     48 import java.io.PrintWriter;
     49 import java.util.ArrayList;
     50 import java.util.List;
     51 import java.util.concurrent.atomic.AtomicInteger;
     52 
     53 /**
     54  * {@hide}
     55  *
     56  * DataConnection StateMachine.
     57  *
     58  * This a class for representing a single data connection, with instances of this
     59  * class representing a connection via the cellular network. There may be multiple
     60  * data connections and all of them are managed by the <code>DataConnectionTracker</code>.
     61  *
     62  * A recent change is to move retry handling into this class, with that change the
     63  * old retry manager is now used internally rather than exposed to the DCT. Also,
     64  * bringUp now has an initialRetry which is used limit the number of retries
     65  * during the initial bring up of the connection. After the connection becomes active
     66  * the current max retry is restored to the configured value.
     67  *
     68  * NOTE: All DataConnection objects must be running on the same looper, which is the default
     69  * as the coordinator has members which are used without synchronization.
     70  */
     71 public final class DataConnection extends StateMachine {
     72     private static final boolean DBG = true;
     73     private static final boolean VDBG = true;
     74 
     75     /** Retry configuration: A doubling of retry times from 5secs to 30minutes */
     76     private static final String DEFAULT_DATA_RETRY_CONFIG = "default_randomization=2000,"
     77         + "5000,10000,20000,40000,80000:5000,160000:5000,"
     78         + "320000:5000,640000:5000,1280000:5000,1800000:5000";
     79 
     80     /** Retry configuration for secondary networks: 4 tries in 20 sec */
     81     private static final String SECONDARY_DATA_RETRY_CONFIG =
     82             "max_retries=3, 5000, 5000, 5000";
     83 
     84     // The data connection controller
     85     private DcController mDcController;
     86 
     87     // The Tester for failing all bringup's
     88     private DcTesterFailBringUpAll mDcTesterFailBringUpAll;
     89 
     90     private static AtomicInteger mInstanceNumber = new AtomicInteger(0);
     91     private AsyncChannel mAc;
     92 
     93     // Utilities for the DataConnection
     94     private DcRetryAlarmController mDcRetryAlarmController;
     95 
     96     // The DCT that's talking to us, we only support one!
     97     private DcTrackerBase mDct = null;
     98 
     99     /**
    100      * Used internally for saving connecting parameters.
    101      */
    102     static class ConnectionParams {
    103         int mTag;
    104         ApnContext mApnContext;
    105         int mInitialMaxRetry;
    106         int mProfileId;
    107         Message mOnCompletedMsg;
    108 
    109         ConnectionParams(ApnContext apnContext, int initialMaxRetry, int profileId,
    110                 Message onCompletedMsg) {
    111             mApnContext = apnContext;
    112             mInitialMaxRetry = initialMaxRetry;
    113             mProfileId = profileId;
    114             mOnCompletedMsg = onCompletedMsg;
    115         }
    116 
    117         @Override
    118         public String toString() {
    119             return "{mTag=" + mTag + " mApnContext=" + mApnContext
    120                     + " mInitialMaxRetry=" + mInitialMaxRetry + " mProfileId=" + mProfileId
    121                     + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}";
    122         }
    123     }
    124 
    125     /**
    126      * Used internally for saving disconnecting parameters.
    127      */
    128     static class DisconnectParams {
    129         int mTag;
    130         ApnContext mApnContext;
    131         String mReason;
    132         Message mOnCompletedMsg;
    133 
    134         DisconnectParams(ApnContext apnContext, String reason, Message onCompletedMsg) {
    135             mApnContext = apnContext;
    136             mReason = reason;
    137             mOnCompletedMsg = onCompletedMsg;
    138         }
    139 
    140         @Override
    141         public String toString() {
    142             return "{mTag=" + mTag + " mApnContext=" + mApnContext
    143                     + " mReason=" + mReason
    144                     + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}";
    145         }
    146     }
    147 
    148     private ApnSetting mApnSetting;
    149     private ConnectionParams mConnectionParams;
    150     private DisconnectParams mDisconnectParams;
    151     private DcFailCause mDcFailCause;
    152 
    153     private PhoneBase mPhone;
    154     private LinkProperties mLinkProperties = new LinkProperties();
    155     private LinkCapabilities mLinkCapabilities = new LinkCapabilities();
    156     private long mCreateTime;
    157     private long mLastFailTime;
    158     private DcFailCause mLastFailCause;
    159     private static final String NULL_IP = "0.0.0.0";
    160     private Object mUserData;
    161 
    162     //***** Package visible variables
    163     int mTag;
    164     int mCid;
    165     List<ApnContext> mApnContexts = null;
    166     PendingIntent mReconnectIntent = null;
    167     RetryManager mRetryManager = new RetryManager();
    168 
    169 
    170     // ***** Event codes for driving the state machine, package visible for Dcc
    171     static final int BASE = Protocol.BASE_DATA_CONNECTION;
    172     static final int EVENT_CONNECT = BASE + 0;
    173     static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1;
    174     static final int EVENT_GET_LAST_FAIL_DONE = BASE + 2;
    175     static final int EVENT_DEACTIVATE_DONE = BASE + 3;
    176     static final int EVENT_DISCONNECT = BASE + 4;
    177     static final int EVENT_RIL_CONNECTED = BASE + 5;
    178     static final int EVENT_DISCONNECT_ALL = BASE + 6;
    179     static final int EVENT_DATA_STATE_CHANGED = BASE + 7;
    180     static final int EVENT_TEAR_DOWN_NOW = BASE + 8;
    181     static final int EVENT_LOST_CONNECTION = BASE + 9;
    182     static final int EVENT_RETRY_CONNECTION = BASE + 10;
    183 
    184     private static final int CMD_TO_STRING_COUNT = EVENT_RETRY_CONNECTION - BASE + 1;
    185     private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
    186     static {
    187         sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT";
    188         sCmdToString[EVENT_SETUP_DATA_CONNECTION_DONE - BASE] =
    189                 "EVENT_SETUP_DATA_CONNECTION_DONE";
    190         sCmdToString[EVENT_GET_LAST_FAIL_DONE - BASE] = "EVENT_GET_LAST_FAIL_DONE";
    191         sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE";
    192         sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT";
    193         sCmdToString[EVENT_RIL_CONNECTED - BASE] = "EVENT_RIL_CONNECTED";
    194         sCmdToString[EVENT_DISCONNECT_ALL - BASE] = "EVENT_DISCONNECT_ALL";
    195         sCmdToString[EVENT_DATA_STATE_CHANGED - BASE] = "EVENT_DATA_STATE_CHANGED";
    196         sCmdToString[EVENT_TEAR_DOWN_NOW - BASE] = "EVENT_TEAR_DOWN_NOW";
    197         sCmdToString[EVENT_LOST_CONNECTION - BASE] = "EVENT_LOST_CONNECTION";
    198         sCmdToString[EVENT_RETRY_CONNECTION - BASE] = "EVENT_RETRY_CONNECTION";
    199     }
    200     // Convert cmd to string or null if unknown
    201     static String cmdToString(int cmd) {
    202         String value;
    203         cmd -= BASE;
    204         if ((cmd >= 0) && (cmd < sCmdToString.length)) {
    205             value = sCmdToString[cmd];
    206         } else {
    207             value = DcAsyncChannel.cmdToString(cmd + BASE);
    208         }
    209         if (value == null) {
    210             value = "0x" + Integer.toHexString(cmd + BASE);
    211         }
    212         return value;
    213     }
    214 
    215     /**
    216      * Create the connection object
    217      *
    218      * @param phone the Phone
    219      * @param id the connection id
    220      * @return DataConnection that was created.
    221      */
    222     static DataConnection makeDataConnection(PhoneBase phone, int id,
    223             DcTrackerBase dct, DcTesterFailBringUpAll failBringUpAll,
    224             DcController dcc) {
    225         DataConnection dc = new DataConnection(phone,
    226                 "DC-" + mInstanceNumber.incrementAndGet(), id, dct, failBringUpAll, dcc);
    227         dc.start();
    228         if (DBG) dc.log("Made " + dc.getName());
    229         return dc;
    230     }
    231 
    232     void dispose() {
    233         log("dispose: call quiteNow()");
    234         quitNow();
    235     }
    236 
    237     /* Getter functions */
    238 
    239     LinkCapabilities getCopyLinkCapabilities() {
    240         return new LinkCapabilities(mLinkCapabilities);
    241     }
    242 
    243     LinkProperties getCopyLinkProperties() {
    244         return new LinkProperties(mLinkProperties);
    245     }
    246 
    247     boolean getIsInactive() {
    248         return getCurrentState() == mInactiveState;
    249     }
    250 
    251     int getCid() {
    252         return mCid;
    253     }
    254 
    255     ApnSetting getApnSetting() {
    256         return mApnSetting;
    257     }
    258 
    259     void setLinkPropertiesHttpProxy(ProxyProperties proxy) {
    260         mLinkProperties.setHttpProxy(proxy);
    261     }
    262 
    263     static class UpdateLinkPropertyResult {
    264         public DataCallResponse.SetupResult setupResult = DataCallResponse.SetupResult.SUCCESS;
    265         public LinkProperties oldLp;
    266         public LinkProperties newLp;
    267         public UpdateLinkPropertyResult(LinkProperties curLp) {
    268             oldLp = curLp;
    269             newLp = curLp;
    270         }
    271     }
    272 
    273     UpdateLinkPropertyResult updateLinkProperty(DataCallResponse newState) {
    274         UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties);
    275 
    276         if (newState == null) return result;
    277 
    278         DataCallResponse.SetupResult setupResult;
    279         result.newLp = new LinkProperties();
    280 
    281         // set link properties based on data call response
    282         result.setupResult = setLinkProperties(newState, result.newLp);
    283         if (result.setupResult != DataCallResponse.SetupResult.SUCCESS) {
    284             if (DBG) log("updateLinkProperty failed : " + result.setupResult);
    285             return result;
    286         }
    287         // copy HTTP proxy as it is not part DataCallResponse.
    288         result.newLp.setHttpProxy(mLinkProperties.getHttpProxy());
    289 
    290         if (DBG && (! result.oldLp.equals(result.newLp))) {
    291             log("updateLinkProperty old LP=" + result.oldLp);
    292             log("updateLinkProperty new LP=" + result.newLp);
    293         }
    294         mLinkProperties = result.newLp;
    295 
    296         return result;
    297     }
    298 
    299     //***** Constructor (NOTE: uses dcc.getHandler() as its Handler)
    300     private DataConnection(PhoneBase phone, String name, int id,
    301                 DcTrackerBase dct, DcTesterFailBringUpAll failBringUpAll,
    302                 DcController dcc) {
    303         super(name, dcc.getHandler());
    304         setLogRecSize(300);
    305         setLogOnlyTransitions(true);
    306         if (DBG) log("DataConnection constructor E");
    307 
    308         mPhone = phone;
    309         mDct = dct;
    310         mDcTesterFailBringUpAll = failBringUpAll;
    311         mDcController = dcc;
    312         mId = id;
    313         mCid = -1;
    314         mDcRetryAlarmController = new DcRetryAlarmController(mPhone, this);
    315 
    316         addState(mDefaultState);
    317             addState(mInactiveState, mDefaultState);
    318             addState(mActivatingState, mDefaultState);
    319             addState(mRetryingState, mDefaultState);
    320             addState(mActiveState, mDefaultState);
    321             addState(mDisconnectingState, mDefaultState);
    322             addState(mDisconnectingErrorCreatingConnection, mDefaultState);
    323         setInitialState(mInactiveState);
    324 
    325         mApnContexts = new ArrayList<ApnContext>();
    326         if (DBG) log("DataConnection constructor X");
    327     }
    328 
    329     private String getRetryConfig(boolean forDefault) {
    330         int nt = mPhone.getServiceState().getNetworkType();
    331 
    332         if (Build.IS_DEBUGGABLE) {
    333             String config = SystemProperties.get("test.data_retry_config");
    334             if (! TextUtils.isEmpty(config)) {
    335                 return config;
    336             }
    337         }
    338 
    339         if ((nt == TelephonyManager.NETWORK_TYPE_CDMA) ||
    340             (nt == TelephonyManager.NETWORK_TYPE_1xRTT) ||
    341             (nt == TelephonyManager.NETWORK_TYPE_EVDO_0) ||
    342             (nt == TelephonyManager.NETWORK_TYPE_EVDO_A) ||
    343             (nt == TelephonyManager.NETWORK_TYPE_EVDO_B) ||
    344             (nt == TelephonyManager.NETWORK_TYPE_EHRPD)) {
    345             // CDMA variant
    346             return SystemProperties.get("ro.cdma.data_retry_config");
    347         } else {
    348             // Use GSM variant for all others.
    349             if (forDefault) {
    350                 return SystemProperties.get("ro.gsm.data_retry_config");
    351             } else {
    352                 return SystemProperties.get("ro.gsm.2nd_data_retry_config");
    353             }
    354         }
    355     }
    356 
    357     private void configureRetry(boolean forDefault) {
    358         String retryConfig = getRetryConfig(forDefault);
    359 
    360         if (!mRetryManager.configure(retryConfig)) {
    361             if (forDefault) {
    362                 if (!mRetryManager.configure(DEFAULT_DATA_RETRY_CONFIG)) {
    363                     // Should never happen, log an error and default to a simple linear sequence.
    364                     loge("configureRetry: Could not configure using " +
    365                             "DEFAULT_DATA_RETRY_CONFIG=" + DEFAULT_DATA_RETRY_CONFIG);
    366                     mRetryManager.configure(5, 2000, 1000);
    367                 }
    368             } else {
    369                 if (!mRetryManager.configure(SECONDARY_DATA_RETRY_CONFIG)) {
    370                     // Should never happen, log an error and default to a simple sequence.
    371                     loge("configureRetry: Could note configure using " +
    372                             "SECONDARY_DATA_RETRY_CONFIG=" + SECONDARY_DATA_RETRY_CONFIG);
    373                     mRetryManager.configure(5, 2000, 1000);
    374                 }
    375             }
    376         }
    377         if (DBG) {
    378             log("configureRetry: forDefault=" + forDefault + " mRetryManager=" + mRetryManager);
    379         }
    380     }
    381 
    382     /**
    383      * Begin setting up a data connection, calls setupDataCall
    384      * and the ConnectionParams will be returned with the
    385      * EVENT_SETUP_DATA_CONNECTION_DONE AsyncResul.userObj.
    386      *
    387      * @param cp is the connection parameters
    388      */
    389     private void onConnect(ConnectionParams cp) {
    390         if (DBG) log("onConnect: carrier='" + mApnSetting.carrier
    391                 + "' APN='" + mApnSetting.apn
    392                 + "' proxy='" + mApnSetting.proxy + "' port='" + mApnSetting.port + "'");
    393 
    394         // Check if we should fake an error.
    395         if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter  > 0) {
    396             DataCallResponse response = new DataCallResponse();
    397             response.version = mPhone.mCi.getRilVersion();
    398             response.status = mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause.getErrorCode();
    399             response.cid = 0;
    400             response.active = 0;
    401             response.type = "";
    402             response.ifname = "";
    403             response.addresses = new String[0];
    404             response.dnses = new String[0];
    405             response.gateways = new String[0];
    406             response.suggestedRetryTime =
    407                     mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime;
    408 
    409             Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
    410             AsyncResult.forMessage(msg, response, null);
    411             sendMessage(msg);
    412             if (DBG) {
    413                 log("onConnect: FailBringUpAll=" + mDcTesterFailBringUpAll.getDcFailBringUp()
    414                         + " send error response=" + response);
    415             }
    416             mDcTesterFailBringUpAll.getDcFailBringUp().mCounter -= 1;
    417             return;
    418         }
    419 
    420         mCreateTime = -1;
    421         mLastFailTime = -1;
    422         mLastFailCause = DcFailCause.NONE;
    423 
    424         // msg.obj will be returned in AsyncResult.userObj;
    425         Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
    426         msg.obj = cp;
    427 
    428         int authType = mApnSetting.authType;
    429         if (authType == -1) {
    430             authType = TextUtils.isEmpty(mApnSetting.user) ? RILConstants.SETUP_DATA_AUTH_NONE
    431                     : RILConstants.SETUP_DATA_AUTH_PAP_CHAP;
    432         }
    433 
    434         String protocol;
    435         if (mPhone.getServiceState().getRoaming()) {
    436             protocol = mApnSetting.roamingProtocol;
    437         } else {
    438             protocol = mApnSetting.protocol;
    439         }
    440 
    441         mPhone.mCi.setupDataCall(
    442                 Integer.toString(getRilRadioTechnology()),
    443                 Integer.toString(cp.mProfileId),
    444                 mApnSetting.apn, mApnSetting.user, mApnSetting.password,
    445                 Integer.toString(authType),
    446                 protocol, msg);
    447     }
    448 
    449     /**
    450      * TearDown the data connection when the deactivation is complete a Message with
    451      * msg.what == EVENT_DEACTIVATE_DONE and msg.obj == AsyncResult with AsyncResult.obj
    452      * containing the parameter o.
    453      *
    454      * @param o is the object returned in the AsyncResult.obj.
    455      */
    456     private void tearDownData(Object o) {
    457         int discReason = RILConstants.DEACTIVATE_REASON_NONE;
    458         if ((o != null) && (o instanceof DisconnectParams)) {
    459             DisconnectParams dp = (DisconnectParams)o;
    460 
    461             if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)) {
    462                 discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
    463             } else if (TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
    464                 discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
    465             }
    466         }
    467         if (mPhone.mCi.getRadioState().isOn()) {
    468             if (DBG) log("tearDownData radio is on, call deactivateDataCall");
    469             mPhone.mCi.deactivateDataCall(mCid, discReason,
    470                     obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
    471         } else {
    472             if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately");
    473             AsyncResult ar = new AsyncResult(o, null, null);
    474             sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, ar));
    475         }
    476     }
    477 
    478     private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) {
    479         for (ApnContext apnContext : mApnContexts) {
    480             if (apnContext == alreadySent) continue;
    481             if (reason != null) apnContext.setReason(reason);
    482             Message msg = mDct.obtainMessage(event, apnContext);
    483             AsyncResult.forMessage(msg);
    484             msg.sendToTarget();
    485         }
    486     }
    487 
    488     private void notifyAllOfConnected(String reason) {
    489         notifyAllWithEvent(null, DctConstants.EVENT_DATA_SETUP_COMPLETE, reason);
    490     }
    491 
    492     private void notifyAllOfDisconnectDcRetrying(String reason) {
    493         notifyAllWithEvent(null, DctConstants.EVENT_DISCONNECT_DC_RETRYING, reason);
    494     }
    495     private void notifyAllDisconnectCompleted(DcFailCause cause) {
    496         notifyAllWithEvent(null, DctConstants.EVENT_DISCONNECT_DONE, cause.toString());
    497     }
    498 
    499 
    500     /**
    501      * Send the connectionCompletedMsg.
    502      *
    503      * @param cp is the ConnectionParams
    504      * @param cause and if no error the cause is DcFailCause.NONE
    505      * @param sendAll is true if all contexts are to be notified
    506      */
    507     private void notifyConnectCompleted(ConnectionParams cp, DcFailCause cause, boolean sendAll) {
    508         ApnContext alreadySent = null;
    509 
    510         if (cp != null && cp.mOnCompletedMsg != null) {
    511             // Get the completed message but only use it once
    512             Message connectionCompletedMsg = cp.mOnCompletedMsg;
    513             cp.mOnCompletedMsg = null;
    514             if (connectionCompletedMsg.obj instanceof ApnContext) {
    515                 alreadySent = (ApnContext)connectionCompletedMsg.obj;
    516             }
    517 
    518             long timeStamp = System.currentTimeMillis();
    519             connectionCompletedMsg.arg1 = mCid;
    520 
    521             if (cause == DcFailCause.NONE) {
    522                 mCreateTime = timeStamp;
    523                 AsyncResult.forMessage(connectionCompletedMsg);
    524             } else {
    525                 mLastFailCause = cause;
    526                 mLastFailTime = timeStamp;
    527 
    528                 // Return message with a Throwable exception to signify an error.
    529                 if (cause == null) cause = DcFailCause.UNKNOWN;
    530                 AsyncResult.forMessage(connectionCompletedMsg, cause,
    531                         new Throwable(cause.toString()));
    532             }
    533             if (DBG) {
    534                 log("notifyConnectCompleted at " + timeStamp + " cause=" + cause
    535                         + " connectionCompletedMsg=" + msgToString(connectionCompletedMsg));
    536             }
    537 
    538             connectionCompletedMsg.sendToTarget();
    539         }
    540         if (sendAll) {
    541             notifyAllWithEvent(alreadySent, DctConstants.EVENT_DATA_SETUP_COMPLETE_ERROR,
    542                     cause.toString());
    543         }
    544     }
    545 
    546     /**
    547      * Send ar.userObj if its a message, which is should be back to originator.
    548      *
    549      * @param dp is the DisconnectParams.
    550      */
    551     private void notifyDisconnectCompleted(DisconnectParams dp, boolean sendAll) {
    552         if (VDBG) log("NotifyDisconnectCompleted");
    553 
    554         ApnContext alreadySent = null;
    555         String reason = null;
    556 
    557         if (dp != null && dp.mOnCompletedMsg != null) {
    558             // Get the completed message but only use it once
    559             Message msg = dp.mOnCompletedMsg;
    560             dp.mOnCompletedMsg = null;
    561             if (msg.obj instanceof ApnContext) {
    562                 alreadySent = (ApnContext)msg.obj;
    563             }
    564             reason = dp.mReason;
    565             if (VDBG) {
    566                 log(String.format("msg=%s msg.obj=%s", msg.toString(),
    567                     ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
    568             }
    569             AsyncResult.forMessage(msg);
    570             msg.sendToTarget();
    571         }
    572         if (sendAll) {
    573             if (reason == null) {
    574                 reason = DcFailCause.UNKNOWN.toString();
    575             }
    576             notifyAllWithEvent(alreadySent, DctConstants.EVENT_DISCONNECT_DONE, reason);
    577         }
    578         if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
    579     }
    580 
    581     private int getRilRadioTechnology() {
    582         int rilRadioTechnology;
    583         if (mApnSetting.bearer > 0) {
    584             rilRadioTechnology = mApnSetting.bearer + 2;
    585         } else {
    586             rilRadioTechnology = mPhone.getServiceState().getRilDataRadioTechnology() + 2;
    587         }
    588         return rilRadioTechnology;
    589     }
    590 
    591     /*
    592      * **************************************************************************
    593      * Begin Members and methods owned by DataConnectionTracker but stored
    594      * in a DataConnection because there is one per connection.
    595      * **************************************************************************
    596      */
    597 
    598     /*
    599      * The id is owned by DataConnectionTracker.
    600      */
    601     private int mId;
    602 
    603     /**
    604      * Get the DataConnection ID
    605      */
    606     public int getDataConnectionId() {
    607         return mId;
    608     }
    609 
    610     /*
    611      * **************************************************************************
    612      * End members owned by DataConnectionTracker
    613      * **************************************************************************
    614      */
    615 
    616     /**
    617      * Clear all settings called when entering mInactiveState.
    618      */
    619     private void clearSettings() {
    620         if (DBG) log("clearSettings");
    621 
    622         mCreateTime = -1;
    623         mLastFailTime = -1;
    624         mLastFailCause = DcFailCause.NONE;
    625         mCid = -1;
    626 
    627         mLinkProperties = new LinkProperties();
    628         mApnContexts.clear();
    629         mApnSetting = null;
    630         mDcFailCause = null;
    631     }
    632 
    633     /**
    634      * Process setup completion.
    635      *
    636      * @param ar is the result
    637      * @return SetupResult.
    638      */
    639     private DataCallResponse.SetupResult onSetupConnectionCompleted(AsyncResult ar) {
    640         DataCallResponse response = (DataCallResponse) ar.result;
    641         ConnectionParams cp = (ConnectionParams) ar.userObj;
    642         DataCallResponse.SetupResult result;
    643 
    644         if (cp.mTag != mTag) {
    645             if (DBG) {
    646                 log("onSetupConnectionCompleted stale cp.tag=" + cp.mTag + ", mtag=" + mTag);
    647             }
    648             result = DataCallResponse.SetupResult.ERR_Stale;
    649         } else if (ar.exception != null) {
    650             if (DBG) {
    651                 log("onSetupConnectionCompleted failed, ar.exception=" + ar.exception +
    652                     " response=" + response);
    653             }
    654 
    655             if (ar.exception instanceof CommandException
    656                     && ((CommandException) (ar.exception)).getCommandError()
    657                     == CommandException.Error.RADIO_NOT_AVAILABLE) {
    658                 result = DataCallResponse.SetupResult.ERR_BadCommand;
    659                 result.mFailCause = DcFailCause.RADIO_NOT_AVAILABLE;
    660             } else if ((response == null) || (response.version < 4)) {
    661                 result = DataCallResponse.SetupResult.ERR_GetLastErrorFromRil;
    662             } else {
    663                 result = DataCallResponse.SetupResult.ERR_RilError;
    664                 result.mFailCause = DcFailCause.fromInt(response.status);
    665             }
    666         } else if (response.status != 0) {
    667             result = DataCallResponse.SetupResult.ERR_RilError;
    668             result.mFailCause = DcFailCause.fromInt(response.status);
    669         } else {
    670             if (DBG) log("onSetupConnectionCompleted received DataCallResponse: " + response);
    671             mCid = response.cid;
    672             result = updateLinkProperty(response).setupResult;
    673         }
    674 
    675         return result;
    676     }
    677 
    678     private boolean isDnsOk(String[] domainNameServers) {
    679         if (NULL_IP.equals(domainNameServers[0]) && NULL_IP.equals(domainNameServers[1])
    680                 && !mPhone.isDnsCheckDisabled()) {
    681             // Work around a race condition where QMI does not fill in DNS:
    682             // Deactivate PDP and let DataConnectionTracker retry.
    683             // Do not apply the race condition workaround for MMS APN
    684             // if Proxy is an IP-address.
    685             // Otherwise, the default APN will not be restored anymore.
    686             if (!mApnSetting.types[0].equals(PhoneConstants.APN_TYPE_MMS)
    687                 || !isIpAddress(mApnSetting.mmsProxy)) {
    688                 log(String.format(
    689                         "isDnsOk: return false apn.types[0]=%s APN_TYPE_MMS=%s isIpAddress(%s)=%s",
    690                         mApnSetting.types[0], PhoneConstants.APN_TYPE_MMS, mApnSetting.mmsProxy,
    691                         isIpAddress(mApnSetting.mmsProxy)));
    692                 return false;
    693             }
    694         }
    695         return true;
    696     }
    697 
    698     private boolean isIpAddress(String address) {
    699         if (address == null) return false;
    700 
    701         return Patterns.IP_ADDRESS.matcher(address).matches();
    702     }
    703 
    704     private DataCallResponse.SetupResult setLinkProperties(DataCallResponse response,
    705             LinkProperties lp) {
    706         // Check if system property dns usable
    707         boolean okToUseSystemPropertyDns = false;
    708         String propertyPrefix = "net." + response.ifname + ".";
    709         String dnsServers[] = new String[2];
    710         dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
    711         dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
    712         okToUseSystemPropertyDns = isDnsOk(dnsServers);
    713 
    714         // set link properties based on data call response
    715         return response.setLinkProperties(lp, okToUseSystemPropertyDns);
    716     }
    717 
    718     /**
    719      * Initialize connection, this will fail if the
    720      * apnSettings are not compatible.
    721      *
    722      * @param cp the Connection paramemters
    723      * @return true if initialization was successful.
    724      */
    725     private boolean initConnection(ConnectionParams cp) {
    726         ApnContext apnContext = cp.mApnContext;
    727         if (mApnSetting == null) {
    728             // Only change apn setting if it isn't set, it will
    729             // only NOT be set only if we're in DcInactiveState.
    730             mApnSetting = apnContext.getApnSetting();
    731         } else if (mApnSetting.canHandleType(apnContext.getApnType())) {
    732             // All is good.
    733         } else {
    734             if (DBG) {
    735                 log("initConnection: incompatible apnSetting in ConnectionParams cp=" + cp
    736                         + " dc=" + DataConnection.this);
    737             }
    738             return false;
    739         }
    740         mTag += 1;
    741         mConnectionParams = cp;
    742         mConnectionParams.mTag = mTag;
    743 
    744         if (!mApnContexts.contains(apnContext)) {
    745             mApnContexts.add(apnContext);
    746         }
    747         configureRetry(mApnSetting.canHandleType(PhoneConstants.APN_TYPE_DEFAULT));
    748         mRetryManager.setRetryCount(0);
    749         mRetryManager.setCurMaxRetryCount(mConnectionParams.mInitialMaxRetry);
    750         mRetryManager.setRetryForever(false);
    751 
    752         if (DBG) {
    753             log("initConnection: "
    754                     + " RefCount=" + mApnContexts.size()
    755                     + " mApnList=" + mApnContexts
    756                     + " mConnectionParams=" + mConnectionParams);
    757         }
    758         return true;
    759     }
    760 
    761     /**
    762      * The parent state for all other states.
    763      */
    764     private class DcDefaultState extends State {
    765         @Override
    766         public void enter() {
    767             if (DBG) log("DcDefaultState: enter");
    768 
    769             // Add ourselves to the list of data connections
    770             mDcController.addDc(DataConnection.this);
    771         }
    772         @Override
    773         public void exit() {
    774             if (DBG) log("DcDefaultState: exit");
    775 
    776 
    777             if (mAc != null) {
    778                 mAc.disconnected();
    779                 mAc = null;
    780             }
    781             mDcRetryAlarmController.dispose();
    782             mDcRetryAlarmController = null;
    783             mApnContexts = null;
    784             mReconnectIntent = null;
    785             mDct = null;
    786             mApnSetting = null;
    787             mPhone = null;
    788             mLinkProperties = null;
    789             mLinkCapabilities = null;
    790             mLastFailCause = null;
    791             mUserData = null;
    792 
    793             // Remove ourselves from the DC lists
    794             mDcController.removeDc(DataConnection.this);
    795 
    796             mDcController = null;
    797             mDcTesterFailBringUpAll = null;
    798         }
    799 
    800         @Override
    801         public boolean processMessage(Message msg) {
    802             boolean retVal = HANDLED;
    803             AsyncResult ar;
    804 
    805             if (VDBG) {
    806                 log("DcDefault msg=" + getWhatToString(msg.what)
    807                         + " RefCount=" + mApnContexts.size());
    808             }
    809             switch (msg.what) {
    810                 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
    811                     if (mAc != null) {
    812                         if (VDBG) log("Disconnecting to previous connection mAc=" + mAc);
    813                         mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
    814                                 AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
    815                     } else {
    816                         mAc = new AsyncChannel();
    817                         mAc.connected(null, getHandler(), msg.replyTo);
    818                         if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected");
    819                         mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
    820                                 AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
    821                     }
    822                     break;
    823                 }
    824                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
    825                     if (VDBG) log("CMD_CHANNEL_DISCONNECTED");
    826                     quit();
    827                     break;
    828                 }
    829                 case DcAsyncChannel.REQ_IS_INACTIVE: {
    830                     boolean val = getIsInactive();
    831                     if (VDBG) log("REQ_IS_INACTIVE  isInactive=" + val);
    832                     mAc.replyToMessage(msg, DcAsyncChannel.RSP_IS_INACTIVE, val ? 1 : 0);
    833                     break;
    834                 }
    835                 case DcAsyncChannel.REQ_GET_CID: {
    836                     int cid = getCid();
    837                     if (VDBG) log("REQ_GET_CID  cid=" + cid);
    838                     mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_CID, cid);
    839                     break;
    840                 }
    841                 case DcAsyncChannel.REQ_GET_APNSETTING: {
    842                     ApnSetting apnSetting = getApnSetting();
    843                     if (VDBG) log("REQ_GET_APNSETTING  mApnSetting=" + apnSetting);
    844                     mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_APNSETTING, apnSetting);
    845                     break;
    846                 }
    847                 case DcAsyncChannel.REQ_GET_LINK_PROPERTIES: {
    848                     LinkProperties lp = getCopyLinkProperties();
    849                     if (VDBG) log("REQ_GET_LINK_PROPERTIES linkProperties" + lp);
    850                     mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_LINK_PROPERTIES, lp);
    851                     break;
    852                 }
    853                 case DcAsyncChannel.REQ_SET_LINK_PROPERTIES_HTTP_PROXY: {
    854                     ProxyProperties proxy = (ProxyProperties) msg.obj;
    855                     if (VDBG) log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy);
    856                     setLinkPropertiesHttpProxy(proxy);
    857                     mAc.replyToMessage(msg, DcAsyncChannel.RSP_SET_LINK_PROPERTIES_HTTP_PROXY);
    858                     break;
    859                 }
    860                 case DcAsyncChannel.REQ_GET_LINK_CAPABILITIES: {
    861                     LinkCapabilities lc = getCopyLinkCapabilities();
    862                     if (VDBG) log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
    863                     mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_LINK_CAPABILITIES, lc);
    864                     break;
    865                 }
    866                 case DcAsyncChannel.REQ_RESET:
    867                     if (VDBG) log("DcDefaultState: msg.what=REQ_RESET");
    868                     transitionTo(mInactiveState);
    869                     break;
    870                 case EVENT_CONNECT:
    871                     if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
    872                     ConnectionParams cp = (ConnectionParams) msg.obj;
    873                     notifyConnectCompleted(cp, DcFailCause.UNKNOWN, false);
    874                     break;
    875 
    876                 case EVENT_DISCONNECT:
    877                     if (DBG) {
    878                         log("DcDefaultState deferring msg.what=EVENT_DISCONNECT RefCount="
    879                                 + mApnContexts.size());
    880                     }
    881                     deferMessage(msg);
    882                     break;
    883 
    884                 case EVENT_DISCONNECT_ALL:
    885                     if (DBG) {
    886                         log("DcDefaultState deferring msg.what=EVENT_DISCONNECT_ALL RefCount="
    887                                 + mApnContexts.size());
    888                     }
    889                     deferMessage(msg);
    890                     break;
    891 
    892                 case EVENT_TEAR_DOWN_NOW:
    893                     if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW");
    894                     mPhone.mCi.deactivateDataCall(mCid, 0,  null);
    895                     break;
    896 
    897                 case EVENT_LOST_CONNECTION:
    898                     if (DBG) {
    899                         String s = "DcDefaultState ignore EVENT_LOST_CONNECTION"
    900                             + " tag=" + msg.arg1 + ":mTag=" + mTag;
    901                         logAndAddLogRec(s);
    902                     }
    903                     break;
    904 
    905                 case EVENT_RETRY_CONNECTION:
    906                     if (DBG) {
    907                         String s = "DcDefaultState ignore EVENT_RETRY_CONNECTION"
    908                                 + " tag=" + msg.arg1 + ":mTag=" + mTag;
    909                         logAndAddLogRec(s);
    910                     }
    911                     break;
    912 
    913                 default:
    914                     if (DBG) {
    915                         log("DcDefaultState: shouldn't happen but ignore msg.what="
    916                                 + getWhatToString(msg.what));
    917                     }
    918                     break;
    919             }
    920 
    921             return retVal;
    922         }
    923     }
    924     private DcDefaultState mDefaultState = new DcDefaultState();
    925 
    926     /**
    927      * The state machine is inactive and expects a EVENT_CONNECT.
    928      */
    929     private class DcInactiveState extends State {
    930         // Inform all contexts we've failed connecting
    931         public void setEnterNotificationParams(ConnectionParams cp, DcFailCause cause) {
    932             if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
    933             mConnectionParams = cp;
    934             mDisconnectParams = null;
    935             mDcFailCause = cause;
    936         }
    937 
    938         // Inform all contexts we've failed disconnected
    939         public void setEnterNotificationParams(DisconnectParams dp) {
    940             if (VDBG) log("DcInactiveState: setEnterNoticationParams dp");
    941             mConnectionParams = null;
    942             mDisconnectParams = dp;
    943             mDcFailCause = DcFailCause.NONE;
    944         }
    945 
    946         // Inform all contexts of the failure cause
    947         public void setEnterNotificationParams(DcFailCause cause) {
    948             mConnectionParams = null;
    949             mDisconnectParams = null;
    950             mDcFailCause = cause;
    951         }
    952 
    953         @Override
    954         public void enter() {
    955             mTag += 1;
    956             if (DBG) log("DcInactiveState: enter() mTag=" + mTag);
    957 
    958             if (mConnectionParams != null) {
    959                 if (DBG) {
    960                     log("DcInactiveState: enter notifyConnectCompleted +ALL failCause="
    961                             + mDcFailCause);
    962                 }
    963                 notifyConnectCompleted(mConnectionParams, mDcFailCause, true);
    964             }
    965             if (mDisconnectParams != null) {
    966                 if (DBG) {
    967                     log("DcInactiveState: enter notifyDisconnectCompleted +ALL failCause="
    968                             + mDcFailCause);
    969                 }
    970                 notifyDisconnectCompleted(mDisconnectParams, true);
    971             }
    972             if (mDisconnectParams == null && mConnectionParams == null && mDcFailCause != null) {
    973                 if (DBG) {
    974                     log("DcInactiveState: enter notifyAllDisconnectCompleted failCause="
    975                             + mDcFailCause);
    976                 }
    977                 notifyAllDisconnectCompleted(mDcFailCause);
    978             }
    979 
    980             // Remove ourselves from cid mapping, before clearSettings
    981             mDcController.removeActiveDcByCid(DataConnection.this);
    982 
    983             clearSettings();
    984         }
    985 
    986         @Override
    987         public void exit() {
    988         }
    989 
    990         @Override
    991         public boolean processMessage(Message msg) {
    992             boolean retVal;
    993 
    994             switch (msg.what) {
    995                 case DcAsyncChannel.REQ_RESET:
    996                     if (DBG) {
    997                         log("DcInactiveState: msg.what=RSP_RESET, ignore we're already reset");
    998                     }
    999                     retVal = HANDLED;
   1000                     break;
   1001 
   1002                 case EVENT_CONNECT:
   1003                     if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT");
   1004                     ConnectionParams cp = (ConnectionParams) msg.obj;
   1005                     if (initConnection(cp)) {
   1006                         onConnect(mConnectionParams);
   1007                         transitionTo(mActivatingState);
   1008                     } else {
   1009                         if (DBG) {
   1010                             log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed");
   1011                         }
   1012                         notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
   1013                                 false);
   1014                     }
   1015                     retVal = HANDLED;
   1016                     break;
   1017 
   1018                 case EVENT_DISCONNECT:
   1019                     if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT");
   1020                     notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
   1021                     retVal = HANDLED;
   1022                     break;
   1023 
   1024                 case EVENT_DISCONNECT_ALL:
   1025                     if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT_ALL");
   1026                     notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
   1027                     retVal = HANDLED;
   1028                     break;
   1029 
   1030                 default:
   1031                     if (VDBG) {
   1032                         log("DcInactiveState nothandled msg.what=" + getWhatToString(msg.what));
   1033                     }
   1034                     retVal = NOT_HANDLED;
   1035                     break;
   1036             }
   1037             return retVal;
   1038         }
   1039     }
   1040     private DcInactiveState mInactiveState = new DcInactiveState();
   1041 
   1042     /**
   1043      * The state machine is retrying and expects a EVENT_RETRY_CONNECTION.
   1044      */
   1045     private class DcRetryingState extends State {
   1046         @Override
   1047         public void enter() {
   1048             if (DBG) {
   1049                 log("DcRetryingState: enter() mTag=" + mTag
   1050                     + ", call notifyAllOfDisconnectDcRetrying lostConnection");
   1051             }
   1052 
   1053             notifyAllOfDisconnectDcRetrying(Phone.REASON_LOST_DATA_CONNECTION);
   1054 
   1055             // Remove ourselves from cid mapping
   1056             mDcController.removeActiveDcByCid(DataConnection.this);
   1057             mCid = -1;
   1058         }
   1059 
   1060         @Override
   1061         public boolean processMessage(Message msg) {
   1062             boolean retVal;
   1063 
   1064             switch (msg.what) {
   1065                 case EVENT_RETRY_CONNECTION: {
   1066                     if (msg.arg1 == mTag) {
   1067                         mRetryManager.increaseRetryCount();
   1068                         if (DBG) {
   1069                             log("DcRetryingState EVENT_RETRY_CONNECTION"
   1070                                     + " RetryCount=" +  mRetryManager.getRetryCount()
   1071                                     + " mConnectionParams=" + mConnectionParams);
   1072                         }
   1073                         onConnect(mConnectionParams);
   1074                         transitionTo(mActivatingState);
   1075                     } else {
   1076                         if (DBG) {
   1077                             log("DcRetryingState stale EVENT_RETRY_CONNECTION"
   1078                                     + " tag:" + msg.arg1 + " != mTag:" + mTag);
   1079                         }
   1080                     }
   1081                     retVal = HANDLED;
   1082                     break;
   1083                 }
   1084                 case DcAsyncChannel.REQ_RESET: {
   1085                     if (DBG) {
   1086                         log("DcRetryingState: msg.what=RSP_RESET, ignore we're already reset");
   1087                     }
   1088                     mInactiveState.setEnterNotificationParams(mConnectionParams,
   1089                             DcFailCause.RESET_BY_FRAMEWORK);
   1090                     transitionTo(mInactiveState);
   1091                     retVal = HANDLED;
   1092                     break;
   1093                 }
   1094                 case EVENT_CONNECT: {
   1095                     ConnectionParams cp = (ConnectionParams) msg.obj;
   1096                     if (DBG) {
   1097                         log("DcRetryingState: msg.what=EVENT_CONNECT"
   1098                                 + " RefCount=" + mApnContexts.size() + " cp=" + cp
   1099                                 + " mConnectionParams=" + mConnectionParams);
   1100                     }
   1101                     if (initConnection(cp)) {
   1102                         onConnect(mConnectionParams);
   1103                         transitionTo(mActivatingState);
   1104                     } else {
   1105                         if (DBG) {
   1106                             log("DcRetryingState: msg.what=EVENT_CONNECT initConnection failed");
   1107                         }
   1108                         notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
   1109                                 false);
   1110                     }
   1111                     retVal = HANDLED;
   1112                     break;
   1113                 }
   1114                 case EVENT_DISCONNECT: {
   1115                     DisconnectParams dp = (DisconnectParams) msg.obj;
   1116 
   1117                     if (mApnContexts.remove(dp.mApnContext) && mApnContexts.size() == 0) {
   1118                         if (DBG) {
   1119                             log("DcRetryingState msg.what=EVENT_DISCONNECT " + " RefCount="
   1120                                     + mApnContexts.size() + " dp=" + dp);
   1121                         }
   1122                         mInactiveState.setEnterNotificationParams(dp);
   1123                         transitionTo(mInactiveState);
   1124                     } else {
   1125                         if (DBG) log("DcRetryingState: msg.what=EVENT_DISCONNECT");
   1126                         notifyDisconnectCompleted(dp, false);
   1127                     }
   1128                     retVal = HANDLED;
   1129                     break;
   1130                 }
   1131                 case EVENT_DISCONNECT_ALL: {
   1132                     if (DBG) {
   1133                         log("DcRetryingState msg.what=EVENT_DISCONNECT/DISCONNECT_ALL "
   1134                                 + "RefCount=" + mApnContexts.size());
   1135                     }
   1136                     mInactiveState.setEnterNotificationParams(DcFailCause.LOST_CONNECTION);
   1137                     deferMessage(msg);
   1138                     transitionTo(mInactiveState);
   1139                     retVal = HANDLED;
   1140                     break;
   1141                 }
   1142                 default: {
   1143                     if (VDBG) {
   1144                         log("DcRetryingState nothandled msg.what=" + getWhatToString(msg.what));
   1145                     }
   1146                     retVal = NOT_HANDLED;
   1147                     break;
   1148                 }
   1149             }
   1150             return retVal;
   1151         }
   1152     }
   1153     private DcRetryingState mRetryingState = new DcRetryingState();
   1154 
   1155     /**
   1156      * The state machine is activating a connection.
   1157      */
   1158     private class DcActivatingState extends State {
   1159         @Override
   1160         public boolean processMessage(Message msg) {
   1161             boolean retVal;
   1162             AsyncResult ar;
   1163             ConnectionParams cp;
   1164 
   1165             if (DBG) log("DcActivatingState: msg=" + msgToString(msg));
   1166             switch (msg.what) {
   1167                 case EVENT_CONNECT:
   1168                     deferMessage(msg);
   1169                     retVal = HANDLED;
   1170                     break;
   1171 
   1172                 case EVENT_SETUP_DATA_CONNECTION_DONE:
   1173                     ar = (AsyncResult) msg.obj;
   1174                     cp = (ConnectionParams) ar.userObj;
   1175 
   1176                     DataCallResponse.SetupResult result = onSetupConnectionCompleted(ar);
   1177                     if (result != DataCallResponse.SetupResult.ERR_Stale) {
   1178                         if (mConnectionParams != cp) {
   1179                             loge("DcActivatingState: WEIRD mConnectionsParams:"+ mConnectionParams
   1180                                     + " != cp:" + cp);
   1181                         }
   1182                     }
   1183                     if (DBG) {
   1184                         log("DcActivatingState onSetupConnectionCompleted result=" + result
   1185                                 + " dc=" + DataConnection.this);
   1186                     }
   1187                     switch (result) {
   1188                         case SUCCESS:
   1189                             // All is well
   1190                             mDcFailCause = DcFailCause.NONE;
   1191                             transitionTo(mActiveState);
   1192                             break;
   1193                         case ERR_BadCommand:
   1194                             // Vendor ril rejected the command and didn't connect.
   1195                             // Transition to inactive but send notifications after
   1196                             // we've entered the mInactive state.
   1197                             mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
   1198                             transitionTo(mInactiveState);
   1199                             break;
   1200                         case ERR_UnacceptableParameter:
   1201                             // The addresses given from the RIL are bad
   1202                             tearDownData(cp);
   1203                             transitionTo(mDisconnectingErrorCreatingConnection);
   1204                             break;
   1205                         case ERR_GetLastErrorFromRil:
   1206                             // Request failed and this is an old RIL
   1207                             mPhone.mCi.getLastDataCallFailCause(
   1208                                     obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp));
   1209                             break;
   1210                         case ERR_RilError:
   1211                             int delay = mDcRetryAlarmController.getSuggestedRetryTime(
   1212                                                                     DataConnection.this, ar);
   1213                             if (DBG) {
   1214                                 log("DcActivatingState: ERR_RilError "
   1215                                         + " delay=" + delay
   1216                                         + " isRetryNeeded=" + mRetryManager.isRetryNeeded()
   1217                                         + " result=" + result
   1218                                         + " result.isRestartRadioFail=" +
   1219                                                 result.mFailCause.isRestartRadioFail()
   1220                                         + " result.isPermanentFail=" +
   1221                                                 result.mFailCause.isPermanentFail());
   1222                             }
   1223                             if (result.mFailCause.isRestartRadioFail()) {
   1224                                 if (DBG) log("DcActivatingState: ERR_RilError restart radio");
   1225                                 mDct.sendRestartRadio();
   1226                                 mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
   1227                                 transitionTo(mInactiveState);
   1228                             } else if (result.mFailCause.isPermanentFail()) {
   1229                                 if (DBG) log("DcActivatingState: ERR_RilError perm error");
   1230                                 mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
   1231                                 transitionTo(mInactiveState);
   1232                             } else if (delay >= 0) {
   1233                                 if (DBG) log("DcActivatingState: ERR_RilError retry");
   1234                                 mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION,
   1235                                                             mTag, delay);
   1236                                 transitionTo(mRetryingState);
   1237                             } else {
   1238                                 if (DBG) log("DcActivatingState: ERR_RilError no retry");
   1239                                 mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
   1240                                 transitionTo(mInactiveState);
   1241                             }
   1242                             break;
   1243                         case ERR_Stale:
   1244                             loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE"
   1245                                     + " tag:" + cp.mTag + " != mTag:" + mTag);
   1246                             break;
   1247                         default:
   1248                             throw new RuntimeException("Unknown SetupResult, should not happen");
   1249                     }
   1250                     retVal = HANDLED;
   1251                     break;
   1252 
   1253                 case EVENT_GET_LAST_FAIL_DONE:
   1254                     ar = (AsyncResult) msg.obj;
   1255                     cp = (ConnectionParams) ar.userObj;
   1256                     if (cp.mTag == mTag) {
   1257                         if (mConnectionParams != cp) {
   1258                             loge("DcActivatingState: WEIRD mConnectionsParams:" + mConnectionParams
   1259                                     + " != cp:" + cp);
   1260                         }
   1261 
   1262                         DcFailCause cause = DcFailCause.UNKNOWN;
   1263 
   1264                         if (ar.exception == null) {
   1265                             int rilFailCause = ((int[]) (ar.result))[0];
   1266                             cause = DcFailCause.fromInt(rilFailCause);
   1267                         }
   1268                         mDcFailCause = cause;
   1269 
   1270                         int retryDelay = mRetryManager.getRetryTimer();
   1271                         if (DBG) {
   1272                             log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"
   1273                                     + " cause=" + cause
   1274                                     + " retryDelay=" + retryDelay
   1275                                     + " isRetryNeeded=" + mRetryManager.isRetryNeeded()
   1276                                     + " dc=" + DataConnection.this);
   1277                         }
   1278                         if (cause.isRestartRadioFail()) {
   1279                             if (DBG) {
   1280                                 log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE"
   1281                                         + " restart radio");
   1282                             }
   1283                             mDct.sendRestartRadio();
   1284                             mInactiveState.setEnterNotificationParams(cp, cause);
   1285                             transitionTo(mInactiveState);
   1286                         } else if (cause.isPermanentFail()) {
   1287                             if (DBG) log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE perm er");
   1288                             mInactiveState.setEnterNotificationParams(cp, cause);
   1289                             transitionTo(mInactiveState);
   1290                         } else if ((retryDelay >= 0) && (mRetryManager.isRetryNeeded())) {
   1291                             if (DBG) log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE retry");
   1292                             mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag,
   1293                                                             retryDelay);
   1294                             transitionTo(mRetryingState);
   1295                         } else {
   1296                             if (DBG) log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE no retry");
   1297                             mInactiveState.setEnterNotificationParams(cp, cause);
   1298                             transitionTo(mInactiveState);
   1299                         }
   1300                     } else {
   1301                         loge("DcActivatingState: stale EVENT_GET_LAST_FAIL_DONE"
   1302                                 + " tag:" + cp.mTag + " != mTag:" + mTag);
   1303                     }
   1304 
   1305                     retVal = HANDLED;
   1306                     break;
   1307 
   1308                 default:
   1309                     if (VDBG) {
   1310                         log("DcActivatingState not handled msg.what=" +
   1311                                 getWhatToString(msg.what) + " RefCount=" + mApnContexts.size());
   1312                     }
   1313                     retVal = NOT_HANDLED;
   1314                     break;
   1315             }
   1316             return retVal;
   1317         }
   1318     }
   1319     private DcActivatingState mActivatingState = new DcActivatingState();
   1320 
   1321     /**
   1322      * The state machine is connected, expecting an EVENT_DISCONNECT.
   1323      */
   1324     private class DcActiveState extends State {
   1325         @Override public void enter() {
   1326             if (DBG) log("DcActiveState: enter dc=" + DataConnection.this);
   1327 
   1328             if (mRetryManager.getRetryCount() != 0) {
   1329                 log("DcActiveState: connected after retrying call notifyAllOfConnected");
   1330                 mRetryManager.setRetryCount(0);
   1331             }
   1332             // If we were retrying there maybe more than one, otherwise they'll only be one.
   1333             notifyAllOfConnected(Phone.REASON_CONNECTED);
   1334 
   1335             // If the EVENT_CONNECT set the current max retry restore it here
   1336             // if it didn't then this is effectively a NOP.
   1337             mRetryManager.restoreCurMaxRetryCount();
   1338             mDcController.addActiveDcByCid(DataConnection.this);
   1339         }
   1340 
   1341         @Override
   1342         public void exit() {
   1343             if (DBG) log("DcActiveState: exit dc=" + this);
   1344         }
   1345 
   1346         @Override
   1347         public boolean processMessage(Message msg) {
   1348             boolean retVal;
   1349 
   1350             switch (msg.what) {
   1351                 case EVENT_CONNECT: {
   1352                     ConnectionParams cp = (ConnectionParams) msg.obj;
   1353                     if (DBG) {
   1354                         log("DcActiveState: EVENT_CONNECT cp=" + cp + " dc=" + DataConnection.this);
   1355                     }
   1356                     if (mApnContexts.contains(cp.mApnContext)) {
   1357                         log("DcActiveState ERROR already added apnContext=" + cp.mApnContext);
   1358                     } else {
   1359                         mApnContexts.add(cp.mApnContext);
   1360                         if (DBG) {
   1361                             log("DcActiveState msg.what=EVENT_CONNECT RefCount="
   1362                                     + mApnContexts.size());
   1363                         }
   1364                     }
   1365                     notifyConnectCompleted(cp, DcFailCause.NONE, false);
   1366                     retVal = HANDLED;
   1367                     break;
   1368                 }
   1369                 case EVENT_DISCONNECT: {
   1370                     DisconnectParams dp = (DisconnectParams) msg.obj;
   1371                     if (DBG) {
   1372                         log("DcActiveState: EVENT_DISCONNECT dp=" + dp
   1373                                 + " dc=" + DataConnection.this);
   1374                     }
   1375                     if (mApnContexts.contains(dp.mApnContext)) {
   1376                         if (DBG) {
   1377                             log("DcActiveState msg.what=EVENT_DISCONNECT RefCount="
   1378                                     + mApnContexts.size());
   1379                         }
   1380 
   1381                         if (mApnContexts.size() == 1) {
   1382                             mApnContexts.clear();
   1383                             mDisconnectParams = dp;
   1384                             mConnectionParams = null;
   1385                             dp.mTag = mTag;
   1386                             tearDownData(dp);
   1387                             transitionTo(mDisconnectingState);
   1388                         } else {
   1389                             mApnContexts.remove(dp.mApnContext);
   1390                             notifyDisconnectCompleted(dp, false);
   1391                         }
   1392                     } else {
   1393                         log("DcActiveState ERROR no such apnContext=" + dp.mApnContext
   1394                                 + " in this dc=" + DataConnection.this);
   1395                         notifyDisconnectCompleted(dp, false);
   1396                     }
   1397                     retVal = HANDLED;
   1398                     break;
   1399                 }
   1400                 case EVENT_DISCONNECT_ALL: {
   1401                     if (DBG) {
   1402                         log("DcActiveState EVENT_DISCONNECT clearing apn contexts,"
   1403                                 + " dc=" + DataConnection.this);
   1404                     }
   1405                     mApnContexts.clear();
   1406                     DisconnectParams dp = (DisconnectParams) msg.obj;
   1407                     mDisconnectParams = dp;
   1408                     mConnectionParams = null;
   1409                     dp.mTag = mTag;
   1410                     tearDownData(dp);
   1411                     transitionTo(mDisconnectingState);
   1412                     retVal = HANDLED;
   1413                     break;
   1414                 }
   1415                 case EVENT_LOST_CONNECTION: {
   1416                     if (DBG) {
   1417                         log("DcActiveState EVENT_LOST_CONNECTION dc=" + DataConnection.this);
   1418                     }
   1419                     if (mRetryManager.isRetryNeeded()) {
   1420                         // We're going to retry
   1421                         int delayMillis = mRetryManager.getRetryTimer();
   1422                         if (DBG) {
   1423                             log("DcActiveState EVENT_LOST_CONNECTION startRetryAlarm"
   1424                                     + " mTag=" + mTag + " delay=" + delayMillis + "ms");
   1425                         }
   1426                         mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag,
   1427                                 delayMillis);
   1428                         transitionTo(mRetryingState);
   1429                     } else {
   1430                         mInactiveState.setEnterNotificationParams(DcFailCause.LOST_CONNECTION);
   1431                         transitionTo(mInactiveState);
   1432                     }
   1433                     retVal = HANDLED;
   1434                     break;
   1435                 }
   1436                 default:
   1437                     if (VDBG) {
   1438                         log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
   1439                     }
   1440                     retVal = NOT_HANDLED;
   1441                     break;
   1442             }
   1443             return retVal;
   1444         }
   1445     }
   1446     private DcActiveState mActiveState = new DcActiveState();
   1447 
   1448     /**
   1449      * The state machine is disconnecting.
   1450      */
   1451     private class DcDisconnectingState extends State {
   1452         @Override
   1453         public boolean processMessage(Message msg) {
   1454             boolean retVal;
   1455 
   1456             switch (msg.what) {
   1457                 case EVENT_CONNECT:
   1458                     if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
   1459                             + mApnContexts.size());
   1460                     deferMessage(msg);
   1461                     retVal = HANDLED;
   1462                     break;
   1463 
   1464                 case EVENT_DEACTIVATE_DONE:
   1465                     if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
   1466                             + mApnContexts.size());
   1467                     AsyncResult ar = (AsyncResult) msg.obj;
   1468                     DisconnectParams dp = (DisconnectParams) ar.userObj;
   1469                     if (dp.mTag == mTag) {
   1470                         // Transition to inactive but send notifications after
   1471                         // we've entered the mInactive state.
   1472                         mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
   1473                         transitionTo(mInactiveState);
   1474                     } else {
   1475                         if (DBG) log("DcDisconnectState stale EVENT_DEACTIVATE_DONE"
   1476                                 + " dp.tag=" + dp.mTag + " mTag=" + mTag);
   1477                     }
   1478                     retVal = HANDLED;
   1479                     break;
   1480 
   1481                 default:
   1482                     if (VDBG) {
   1483                         log("DcDisconnectingState not handled msg.what="
   1484                                 + getWhatToString(msg.what));
   1485                     }
   1486                     retVal = NOT_HANDLED;
   1487                     break;
   1488             }
   1489             return retVal;
   1490         }
   1491     }
   1492     private DcDisconnectingState mDisconnectingState = new DcDisconnectingState();
   1493 
   1494     /**
   1495      * The state machine is disconnecting after an creating a connection.
   1496      */
   1497     private class DcDisconnectionErrorCreatingConnection extends State {
   1498         @Override
   1499         public boolean processMessage(Message msg) {
   1500             boolean retVal;
   1501 
   1502             switch (msg.what) {
   1503                 case EVENT_DEACTIVATE_DONE:
   1504                     AsyncResult ar = (AsyncResult) msg.obj;
   1505                     ConnectionParams cp = (ConnectionParams) ar.userObj;
   1506                     if (cp.mTag == mTag) {
   1507                         if (DBG) {
   1508                             log("DcDisconnectionErrorCreatingConnection" +
   1509                                 " msg.what=EVENT_DEACTIVATE_DONE");
   1510                         }
   1511 
   1512                         // Transition to inactive but send notifications after
   1513                         // we've entered the mInactive state.
   1514                         mInactiveState.setEnterNotificationParams(cp,
   1515                                 DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER);
   1516                         transitionTo(mInactiveState);
   1517                     } else {
   1518                         if (DBG) {
   1519                             log("DcDisconnectionErrorCreatingConnection stale EVENT_DEACTIVATE_DONE"
   1520                                     + " dp.tag=" + cp.mTag + ", mTag=" + mTag);
   1521                         }
   1522                     }
   1523                     retVal = HANDLED;
   1524                     break;
   1525 
   1526                 default:
   1527                     if (VDBG) {
   1528                         log("DcDisconnectionErrorCreatingConnection not handled msg.what="
   1529                                 + getWhatToString(msg.what));
   1530                     }
   1531                     retVal = NOT_HANDLED;
   1532                     break;
   1533             }
   1534             return retVal;
   1535         }
   1536     }
   1537     private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection =
   1538                 new DcDisconnectionErrorCreatingConnection();
   1539 
   1540     // ******* "public" interface
   1541 
   1542     /**
   1543      * Used for testing purposes.
   1544      */
   1545     /* package */ void tearDownNow() {
   1546         if (DBG) log("tearDownNow()");
   1547         sendMessage(obtainMessage(EVENT_TEAR_DOWN_NOW));
   1548     }
   1549 
   1550     /**
   1551      * @return the string for msg.what as our info.
   1552      */
   1553     @Override
   1554     protected String getWhatToString(int what) {
   1555         return cmdToString(what);
   1556     }
   1557 
   1558     private static String msgToString(Message msg) {
   1559         String retVal;
   1560         if (msg == null) {
   1561             retVal = "null";
   1562         } else {
   1563             StringBuilder   b = new StringBuilder();
   1564 
   1565             b.append("{what=");
   1566             b.append(cmdToString(msg.what));
   1567 
   1568             b.append(" when=");
   1569             TimeUtils.formatDuration(msg.getWhen() - SystemClock.uptimeMillis(), b);
   1570 
   1571             if (msg.arg1 != 0) {
   1572                 b.append(" arg1=");
   1573                 b.append(msg.arg1);
   1574             }
   1575 
   1576             if (msg.arg2 != 0) {
   1577                 b.append(" arg2=");
   1578                 b.append(msg.arg2);
   1579             }
   1580 
   1581             if (msg.obj != null) {
   1582                 b.append(" obj=");
   1583                 b.append(msg.obj);
   1584             }
   1585 
   1586             b.append(" target=");
   1587             b.append(msg.getTarget());
   1588 
   1589             b.append(" replyTo=");
   1590             b.append(msg.replyTo);
   1591 
   1592             b.append("}");
   1593 
   1594             retVal = b.toString();
   1595         }
   1596         return retVal;
   1597     }
   1598 
   1599     static void slog(String s) {
   1600         Rlog.d("DC", s);
   1601     }
   1602 
   1603     /**
   1604      * Log with debug
   1605      *
   1606      * @param s is string log
   1607      */
   1608     @Override
   1609     protected void log(String s) {
   1610         Rlog.d(getName(), s);
   1611     }
   1612 
   1613     /**
   1614      * Log with debug attribute
   1615      *
   1616      * @param s is string log
   1617      */
   1618     @Override
   1619     protected void logd(String s) {
   1620         Rlog.d(getName(), s);
   1621     }
   1622 
   1623     /**
   1624      * Log with verbose attribute
   1625      *
   1626      * @param s is string log
   1627      */
   1628     @Override
   1629     protected void logv(String s) {
   1630         Rlog.v(getName(), s);
   1631     }
   1632 
   1633     /**
   1634      * Log with info attribute
   1635      *
   1636      * @param s is string log
   1637      */
   1638     @Override
   1639     protected void logi(String s) {
   1640         Rlog.i(getName(), s);
   1641     }
   1642 
   1643     /**
   1644      * Log with warning attribute
   1645      *
   1646      * @param s is string log
   1647      */
   1648     @Override
   1649     protected void logw(String s) {
   1650         Rlog.w(getName(), s);
   1651     }
   1652 
   1653     /**
   1654      * Log with error attribute
   1655      *
   1656      * @param s is string log
   1657      */
   1658     @Override
   1659     protected void loge(String s) {
   1660         Rlog.e(getName(), s);
   1661     }
   1662 
   1663     /**
   1664      * Log with error attribute
   1665      *
   1666      * @param s is string log
   1667      * @param e is a Throwable which logs additional information.
   1668      */
   1669     @Override
   1670     protected void loge(String s, Throwable e) {
   1671         Rlog.e(getName(), s, e);
   1672     }
   1673 
   1674     /** Doesn't print mApnList of ApnContext's which would be recursive */
   1675     public String toStringSimple() {
   1676         return getName() + ": State=" + getCurrentState().getName()
   1677                 + " mApnSetting=" + mApnSetting + " RefCount=" + mApnContexts.size()
   1678                 + " mCid=" + mCid + " mCreateTime=" + mCreateTime
   1679                 + " mLastastFailTime=" + mLastFailTime
   1680                 + " mLastFailCause=" + mLastFailCause
   1681                 + " mTag=" + mTag
   1682                 + " mRetryManager=" + mRetryManager
   1683                 + " mLinkProperties=" + mLinkProperties
   1684                 + " mLinkCapabilities=" + mLinkCapabilities;
   1685     }
   1686 
   1687     @Override
   1688     public String toString() {
   1689         return "{" + toStringSimple() + " mApnContexts=" + mApnContexts + "}";
   1690     }
   1691 
   1692     /**
   1693      * Dump the current state.
   1694      *
   1695      * @param fd
   1696      * @param pw
   1697      * @param args
   1698      */
   1699     @Override
   1700     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1701         pw.print("DataConnection ");
   1702         super.dump(fd, pw, args);
   1703         pw.println(" mApnContexts.size=" + mApnContexts.size());
   1704         pw.println(" mApnContexts=" + mApnContexts);
   1705         pw.flush();
   1706         pw.println(" mDataConnectionTracker=" + mDct);
   1707         pw.println(" mApnSetting=" + mApnSetting);
   1708         pw.println(" mTag=" + mTag);
   1709         pw.println(" mCid=" + mCid);
   1710         pw.println(" mRetryManager=" + mRetryManager);
   1711         pw.println(" mConnectionParams=" + mConnectionParams);
   1712         pw.println(" mDisconnectParams=" + mDisconnectParams);
   1713         pw.println(" mDcFailCause=" + mDcFailCause);
   1714         pw.flush();
   1715         pw.println(" mPhone=" + mPhone);
   1716         pw.flush();
   1717         pw.println(" mLinkProperties=" + mLinkProperties);
   1718         pw.flush();
   1719         pw.println(" mLinkCapabilities=" + mLinkCapabilities);
   1720         pw.println(" mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
   1721         pw.println(" mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime));
   1722         pw.println(" mLastFailCause=" + mLastFailCause);
   1723         pw.flush();
   1724         pw.println(" mUserData=" + mUserData);
   1725         pw.println(" mInstanceNumber=" + mInstanceNumber);
   1726         pw.println(" mAc=" + mAc);
   1727         pw.println(" mDcRetryAlarmController=" + mDcRetryAlarmController);
   1728         pw.flush();
   1729     }
   1730 }
   1731