Home | History | Annotate | Download | only in telephony
      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;
     18 
     19 
     20 import com.android.internal.telephony.DataCallState.SetupResult;
     21 import com.android.internal.util.AsyncChannel;
     22 import com.android.internal.util.Protocol;
     23 import com.android.internal.util.State;
     24 import com.android.internal.util.StateMachine;
     25 
     26 import android.app.PendingIntent;
     27 import android.net.LinkCapabilities;
     28 import android.net.LinkProperties;
     29 import android.net.ProxyProperties;
     30 import android.os.AsyncResult;
     31 import android.os.Message;
     32 import android.os.SystemProperties;
     33 import android.text.TextUtils;
     34 
     35 import java.util.ArrayList;
     36 import java.util.HashMap;
     37 import java.util.List;
     38 
     39 /**
     40  * {@hide}
     41  *
     42  * DataConnection StateMachine.
     43  *
     44  * This is an abstract base class for representing a single data connection.
     45  * Instances of this class such as <code>CdmaDataConnection</code> and
     46  * <code>GsmDataConnection</code>, * represent a connection via the cellular network.
     47  * There may be multiple data connections and all of them are managed by the
     48  * <code>DataConnectionTracker</code>.
     49  *
     50  * Instances are asynchronous state machines and have two primary entry points
     51  * <code>connect()</code> and <code>disconnect</code>. The message a parameter will be returned
     52  * hen the operation completes. The <code>msg.obj</code> will contain an AsyncResult
     53  * object and <code>AsyncResult.userObj</code> is the original <code>msg.obj</code>. if successful
     54  * with the <code>AsyncResult.result == null</code> and <code>AsyncResult.exception == null</code>.
     55  * If an error <code>AsyncResult.result = FailCause</code> and
     56  * <code>AsyncResult.exception = new Exception()</code>.
     57  *
     58  * The other public methods are provided for debugging.
     59  */
     60 public abstract class DataConnection extends StateMachine {
     61     protected static final boolean DBG = true;
     62     protected static final boolean VDBG = false;
     63 
     64     protected static Object mCountLock = new Object();
     65     protected static int mCount;
     66     protected AsyncChannel mAc;
     67 
     68     private List<ApnContext> mApnList = null;
     69     PendingIntent mReconnectIntent = null;
     70 
     71     /**
     72      * Used internally for saving connecting parameters.
     73      */
     74     protected static class ConnectionParams {
     75         public ConnectionParams(ApnSetting apn, Message onCompletedMsg) {
     76             this.apn = apn;
     77             this.onCompletedMsg = onCompletedMsg;
     78         }
     79 
     80         public int tag;
     81         public ApnSetting apn;
     82         public Message onCompletedMsg;
     83     }
     84 
     85     /**
     86      * Used internally for saving disconnecting parameters.
     87      */
     88     protected static class DisconnectParams {
     89         public DisconnectParams(String reason, Message onCompletedMsg) {
     90             this.reason = reason;
     91             this.onCompletedMsg = onCompletedMsg;
     92         }
     93         public int tag;
     94         public String reason;
     95         public Message onCompletedMsg;
     96     }
     97 
     98     /**
     99      * Returned as the reason for a connection failure as defined
    100      * by RIL_DataCallFailCause in ril.h and some local errors.
    101      */
    102     public enum FailCause {
    103         NONE(0),
    104 
    105         // This series of errors as specified by the standards
    106         // specified in ril.h
    107         OPERATOR_BARRED(0x08),
    108         INSUFFICIENT_RESOURCES(0x1A),
    109         MISSING_UNKNOWN_APN(0x1B),
    110         UNKNOWN_PDP_ADDRESS_TYPE(0x1C),
    111         USER_AUTHENTICATION(0x1D),
    112         ACTIVATION_REJECT_GGSN(0x1E),
    113         ACTIVATION_REJECT_UNSPECIFIED(0x1F),
    114         SERVICE_OPTION_NOT_SUPPORTED(0x20),
    115         SERVICE_OPTION_NOT_SUBSCRIBED(0x21),
    116         SERVICE_OPTION_OUT_OF_ORDER(0x22),
    117         NSAPI_IN_USE(0x23),
    118         ONLY_IPV4_ALLOWED(0x32),
    119         ONLY_IPV6_ALLOWED(0x33),
    120         ONLY_SINGLE_BEARER_ALLOWED(0x34),
    121         PROTOCOL_ERRORS(0x6F),
    122 
    123         // Local errors generated by Vendor RIL
    124         // specified in ril.h
    125         REGISTRATION_FAIL(-1),
    126         GPRS_REGISTRATION_FAIL(-2),
    127         SIGNAL_LOST(-3),
    128         PREF_RADIO_TECH_CHANGED(-4),
    129         RADIO_POWER_OFF(-5),
    130         TETHERED_CALL_ACTIVE(-6),
    131         ERROR_UNSPECIFIED(0xFFFF),
    132 
    133         // Errors generated by the Framework
    134         // specified here
    135         UNKNOWN(0x10000),
    136         RADIO_NOT_AVAILABLE(0x10001),
    137         UNACCEPTABLE_NETWORK_PARAMETER(0x10002);
    138 
    139         private final int mErrorCode;
    140         private static final HashMap<Integer, FailCause> sErrorCodeToFailCauseMap;
    141         static {
    142             sErrorCodeToFailCauseMap = new HashMap<Integer, FailCause>();
    143             for (FailCause fc : values()) {
    144                 sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc);
    145             }
    146         }
    147 
    148         FailCause(int errorCode) {
    149             mErrorCode = errorCode;
    150         }
    151 
    152         int getErrorCode() {
    153             return mErrorCode;
    154         }
    155 
    156         public boolean isPermanentFail() {
    157             return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) ||
    158                    (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
    159                    (this == SERVICE_OPTION_NOT_SUPPORTED) ||
    160                    (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) ||
    161                    (this == PROTOCOL_ERRORS);
    162         }
    163 
    164         public boolean isEventLoggable() {
    165             return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) ||
    166                     (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
    167                     (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
    168                     (this == SERVICE_OPTION_NOT_SUBSCRIBED) ||
    169                     (this == SERVICE_OPTION_NOT_SUPPORTED) ||
    170                     (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) ||
    171                     (this == PROTOCOL_ERRORS) ||
    172                     (this == UNACCEPTABLE_NETWORK_PARAMETER);
    173         }
    174 
    175         public static FailCause fromInt(int errorCode) {
    176             FailCause fc = sErrorCodeToFailCauseMap.get(errorCode);
    177             if (fc == null) {
    178                 fc = UNKNOWN;
    179             }
    180             return fc;
    181         }
    182     }
    183 
    184     public static class CallSetupException extends Exception {
    185         private int mRetryOverride = -1;
    186 
    187         CallSetupException (int retryOverride) {
    188             mRetryOverride = retryOverride;
    189         }
    190 
    191         public int getRetryOverride() {
    192             return mRetryOverride;
    193         }
    194     }
    195 
    196     // ***** Event codes for driving the state machine
    197     protected static final int BASE = Protocol.BASE_DATA_CONNECTION;
    198     protected static final int EVENT_CONNECT = BASE + 0;
    199     protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1;
    200     protected static final int EVENT_GET_LAST_FAIL_DONE = BASE + 2;
    201     protected static final int EVENT_DEACTIVATE_DONE = BASE + 3;
    202     protected static final int EVENT_DISCONNECT = BASE + 4;
    203     protected static final int EVENT_RIL_CONNECTED = BASE + 5;
    204 
    205     //***** Tag IDs for EventLog
    206     protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100;
    207 
    208     //***** Member Variables
    209     protected ApnSetting mApn;
    210     protected int mTag;
    211     protected PhoneBase phone;
    212     protected int mRilVersion = -1;
    213     protected int cid;
    214     protected LinkProperties mLinkProperties = new LinkProperties();
    215     protected LinkCapabilities mCapabilities = new LinkCapabilities();
    216     protected long createTime;
    217     protected long lastFailTime;
    218     protected FailCause lastFailCause;
    219     protected int mRetryOverride = -1;
    220     protected static final String NULL_IP = "0.0.0.0";
    221     private int mRefCount;
    222     Object userData;
    223 
    224     //***** Abstract methods
    225     @Override
    226     public abstract String toString();
    227 
    228     protected abstract void onConnect(ConnectionParams cp);
    229 
    230     protected abstract boolean isDnsOk(String[] domainNameServers);
    231 
    232     protected abstract void log(String s);
    233 
    234 
    235    //***** Constructor
    236     protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm) {
    237         super(name);
    238         if (DBG) log("DataConnection constructor E");
    239         this.phone = phone;
    240         mId = id;
    241         mRetryMgr = rm;
    242         this.cid = -1;
    243 
    244         setDbg(false);
    245         addState(mDefaultState);
    246             addState(mInactiveState, mDefaultState);
    247             addState(mActivatingState, mDefaultState);
    248             addState(mActiveState, mDefaultState);
    249             addState(mDisconnectingState, mDefaultState);
    250             addState(mDisconnectingErrorCreatingConnection, mDefaultState);
    251         setInitialState(mInactiveState);
    252 
    253         mApnList = new ArrayList<ApnContext>();
    254         if (DBG) log("DataConnection constructor X");
    255     }
    256 
    257     /**
    258      * TearDown the data connection.
    259      *
    260      * @param o will be returned in AsyncResult.userObj
    261      *          and is either a DisconnectParams or ConnectionParams.
    262      */
    263     private void tearDownData(Object o) {
    264         int discReason = RILConstants.DEACTIVATE_REASON_NONE;
    265         if ((o != null) && (o instanceof DisconnectParams)) {
    266             DisconnectParams dp = (DisconnectParams)o;
    267             Message m = dp.onCompletedMsg;
    268             if (TextUtils.equals(dp.reason, Phone.REASON_RADIO_TURNED_OFF)) {
    269                 discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
    270             } else if (TextUtils.equals(dp.reason, Phone.REASON_PDP_RESET)) {
    271                 discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
    272             }
    273         }
    274         if (phone.mCM.getRadioState().isOn()) {
    275             if (DBG) log("tearDownData radio is on, call deactivateDataCall");
    276             phone.mCM.deactivateDataCall(cid, discReason, obtainMessage(EVENT_DEACTIVATE_DONE, o));
    277         } else {
    278             if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately");
    279             AsyncResult ar = new AsyncResult(o, null, null);
    280             sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, ar));
    281         }
    282     }
    283 
    284     /**
    285      * Send the connectionCompletedMsg.
    286      *
    287      * @param cp is the ConnectionParams
    288      * @param cause
    289      */
    290     private void notifyConnectCompleted(ConnectionParams cp, FailCause cause) {
    291         Message connectionCompletedMsg = cp.onCompletedMsg;
    292         if (connectionCompletedMsg == null) {
    293             return;
    294         }
    295 
    296         long timeStamp = System.currentTimeMillis();
    297         connectionCompletedMsg.arg1 = cid;
    298 
    299         if (cause == FailCause.NONE) {
    300             createTime = timeStamp;
    301             AsyncResult.forMessage(connectionCompletedMsg);
    302         } else {
    303             lastFailCause = cause;
    304             lastFailTime = timeStamp;
    305             AsyncResult.forMessage(connectionCompletedMsg, cause,
    306                                    new CallSetupException(mRetryOverride));
    307         }
    308         if (DBG) log("notifyConnectionCompleted at " + timeStamp + " cause=" + cause);
    309 
    310         connectionCompletedMsg.sendToTarget();
    311     }
    312 
    313     /**
    314      * Send ar.userObj if its a message, which is should be back to originator.
    315      *
    316      * @param dp is the DisconnectParams.
    317      */
    318     private void notifyDisconnectCompleted(DisconnectParams dp) {
    319         if (VDBG) log("NotifyDisconnectCompleted");
    320 
    321         if (dp.onCompletedMsg != null) {
    322             Message msg = dp.onCompletedMsg;
    323             if (VDBG) {
    324                 log(String.format("msg=%s msg.obj=%s", msg.toString(),
    325                     ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
    326             }
    327             AsyncResult.forMessage(msg);
    328             msg.sendToTarget();
    329         }
    330         if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
    331     }
    332 
    333     protected int getRadioTechnology(int defaultRadioTechnology) {
    334         int radioTechnology;
    335         if (mRilVersion < 6) {
    336             radioTechnology = defaultRadioTechnology;
    337         } else {
    338             radioTechnology = phone.getServiceState().getRadioTechnology() + 2;
    339         }
    340         return radioTechnology;
    341     }
    342 
    343     /*
    344      * **************************************************************************
    345      * Begin Members and methods owned by DataConnectionTracker but stored
    346      * in a DataConnection because there is one per connection.
    347      * **************************************************************************
    348      */
    349 
    350     /*
    351      * The id is owned by DataConnectionTracker.
    352      */
    353     private int mId;
    354 
    355     /**
    356      * Get the DataConnection ID
    357      */
    358     public int getDataConnectionId() {
    359         return mId;
    360     }
    361 
    362     /*
    363      * The retry manager is currently owned by the DataConnectionTracker but is stored
    364      * in the DataConnection because there is one per connection. These methods
    365      * should only be used by the DataConnectionTracker although someday the retrying
    366      * maybe managed by the DataConnection itself and these methods could disappear.
    367      */
    368     private RetryManager mRetryMgr;
    369 
    370     /**
    371      * @return retry manager retryCount
    372      */
    373     public int getRetryCount() {
    374         return mRetryMgr.getRetryCount();
    375     }
    376 
    377     /**
    378      * @return retry manager retryTimer
    379      */
    380     public int getRetryTimer() {
    381         return mRetryMgr.getRetryTimer();
    382     }
    383 
    384     /**
    385      * increaseRetryCount of retry manager
    386      */
    387     public void increaseRetryCount() {
    388         mRetryMgr.increaseRetryCount();
    389     }
    390 
    391     /**
    392      * @return retry manager isRetryNeeded
    393      */
    394     public boolean isRetryNeeded() {
    395         return mRetryMgr.isRetryNeeded();
    396     }
    397 
    398     /**
    399      * resetRetryCount of retry manager
    400      */
    401     public void resetRetryCount() {
    402         mRetryMgr.resetRetryCount();
    403     }
    404 
    405     /**
    406      * set retryForeverUsingLasttimeout of retry manager
    407      */
    408     public void retryForeverUsingLastTimeout() {
    409         mRetryMgr.retryForeverUsingLastTimeout();
    410     }
    411 
    412     /**
    413      * @return retry manager isRetryForever
    414      */
    415     public boolean isRetryForever() {
    416         return mRetryMgr.isRetryForever();
    417     }
    418 
    419     /**
    420      * @return whether the retry config is set successfully or not
    421      */
    422     public boolean configureRetry(int maxRetryCount, int retryTime, int randomizationTime) {
    423         return mRetryMgr.configure(maxRetryCount, retryTime, randomizationTime);
    424     }
    425 
    426     /**
    427      * @return whether the retry config is set successfully or not
    428      */
    429     public boolean configureRetry(String configStr) {
    430         return mRetryMgr.configure(configStr);
    431     }
    432 
    433     /*
    434      * **************************************************************************
    435      * End members owned by DataConnectionTracker
    436      * **************************************************************************
    437      */
    438 
    439     /**
    440      * Clear all settings called when entering mInactiveState.
    441      */
    442     protected void clearSettings() {
    443         if (DBG) log("clearSettings");
    444 
    445         createTime = -1;
    446         lastFailTime = -1;
    447         lastFailCause = FailCause.NONE;
    448         mRetryOverride = -1;
    449         mRefCount = 0;
    450         cid = -1;
    451 
    452         mLinkProperties = new LinkProperties();
    453         mApn = null;
    454     }
    455 
    456     /**
    457      * Process setup completion.
    458      *
    459      * @param ar is the result
    460      * @return SetupResult.
    461      */
    462     private DataCallState.SetupResult onSetupConnectionCompleted(AsyncResult ar) {
    463         DataCallState response = (DataCallState) ar.result;
    464         ConnectionParams cp = (ConnectionParams) ar.userObj;
    465         DataCallState.SetupResult result;
    466 
    467         if (ar.exception != null) {
    468             if (DBG) {
    469                 log("onSetupConnectionCompleted failed, ar.exception=" + ar.exception +
    470                     " response=" + response);
    471             }
    472 
    473             if (ar.exception instanceof CommandException
    474                     && ((CommandException) (ar.exception)).getCommandError()
    475                     == CommandException.Error.RADIO_NOT_AVAILABLE) {
    476                 result = DataCallState.SetupResult.ERR_BadCommand;
    477                 result.mFailCause = FailCause.RADIO_NOT_AVAILABLE;
    478             } else if ((response == null) || (response.version < 4)) {
    479                 result = DataCallState.SetupResult.ERR_GetLastErrorFromRil;
    480             } else {
    481                 result = DataCallState.SetupResult.ERR_RilError;
    482                 result.mFailCause = FailCause.fromInt(response.status);
    483             }
    484         } else if (cp.tag != mTag) {
    485             if (DBG) {
    486                 log("BUG: onSetupConnectionCompleted is stale cp.tag=" + cp.tag + ", mtag=" + mTag);
    487             }
    488             result = DataCallState.SetupResult.ERR_Stale;
    489         } else if (response.status != 0) {
    490             result = DataCallState.SetupResult.ERR_RilError;
    491             result.mFailCause = FailCause.fromInt(response.status);
    492         } else {
    493             if (DBG) log("onSetupConnectionCompleted received DataCallState: " + response);
    494             cid = response.cid;
    495             result = updateLinkProperty(response).setupResult;
    496         }
    497 
    498         return result;
    499     }
    500 
    501     private int getSuggestedRetryTime(AsyncResult ar) {
    502         int retry = -1;
    503         if (ar.exception == null) {
    504             DataCallState response = (DataCallState) ar.result;
    505             retry =  response.suggestedRetryTime;
    506         }
    507         return retry;
    508     }
    509 
    510     private DataCallState.SetupResult setLinkProperties(DataCallState response,
    511             LinkProperties lp) {
    512         // Check if system property dns usable
    513         boolean okToUseSystemPropertyDns = false;
    514         String propertyPrefix = "net." + response.ifname + ".";
    515         String dnsServers[] = new String[2];
    516         dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
    517         dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
    518         okToUseSystemPropertyDns = isDnsOk(dnsServers);
    519 
    520         // set link properties based on data call response
    521         return response.setLinkProperties(lp, okToUseSystemPropertyDns);
    522     }
    523 
    524     public static class UpdateLinkPropertyResult {
    525         public DataCallState.SetupResult setupResult = DataCallState.SetupResult.SUCCESS;
    526         public LinkProperties oldLp;
    527         public LinkProperties newLp;
    528         public UpdateLinkPropertyResult(LinkProperties curLp) {
    529             oldLp = curLp;
    530             newLp = curLp;
    531         }
    532     }
    533 
    534     private UpdateLinkPropertyResult updateLinkProperty(DataCallState newState) {
    535         UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties);
    536 
    537         if (newState == null) return result;
    538 
    539         DataCallState.SetupResult setupResult;
    540         result.newLp = new LinkProperties();
    541 
    542         // set link properties based on data call response
    543         result.setupResult = setLinkProperties(newState, result.newLp);
    544         if (result.setupResult != DataCallState.SetupResult.SUCCESS) {
    545             if (DBG) log("updateLinkProperty failed : " + result.setupResult);
    546             return result;
    547         }
    548         // copy HTTP proxy as it is not part DataCallState.
    549         result.newLp.setHttpProxy(mLinkProperties.getHttpProxy());
    550 
    551         if (DBG && (! result.oldLp.equals(result.newLp))) {
    552             if (DBG) log("updateLinkProperty old != new");
    553             if (VDBG) log("updateLinkProperty old LP=" + result.oldLp);
    554             if (VDBG) log("updateLinkProperty new LP=" + result.newLp);
    555         }
    556         mLinkProperties = result.newLp;
    557 
    558         return result;
    559     }
    560 
    561     /**
    562      * The parent state for all other states.
    563      */
    564     private class DcDefaultState extends State {
    565         @Override
    566         public void enter() {
    567             phone.mCM.registerForRilConnected(getHandler(), EVENT_RIL_CONNECTED, null);
    568         }
    569         @Override
    570         public void exit() {
    571             phone.mCM.unregisterForRilConnected(getHandler());
    572         }
    573         @Override
    574         public boolean processMessage(Message msg) {
    575             AsyncResult ar;
    576 
    577             switch (msg.what) {
    578                 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
    579                     if (mAc != null) {
    580                         if (VDBG) log("Disconnecting to previous connection mAc=" + mAc);
    581                         mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
    582                                 AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
    583                     } else {
    584                         mAc = new AsyncChannel();
    585                         mAc.connected(null, getHandler(), msg.replyTo);
    586                         if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected");
    587                         mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
    588                                 AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
    589                     }
    590                     break;
    591                 }
    592                 case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
    593                     if (VDBG) log("CMD_CHANNEL_DISCONNECT");
    594                     mAc.disconnect();
    595                     break;
    596                 }
    597                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
    598                     if (VDBG) log("CMD_CHANNEL_DISCONNECTED");
    599                     mAc = null;
    600                     break;
    601                 }
    602                 case DataConnectionAc.REQ_IS_INACTIVE: {
    603                     boolean val = getCurrentState() == mInactiveState;
    604                     if (VDBG) log("REQ_IS_INACTIVE  isInactive=" + val);
    605                     mAc.replyToMessage(msg, DataConnectionAc.RSP_IS_INACTIVE, val ? 1 : 0);
    606                     break;
    607                 }
    608                 case DataConnectionAc.REQ_GET_CID: {
    609                     if (VDBG) log("REQ_GET_CID  cid=" + cid);
    610                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_CID, cid);
    611                     break;
    612                 }
    613                 case DataConnectionAc.REQ_GET_APNSETTING: {
    614                     if (VDBG) log("REQ_GET_APNSETTING  apnSetting=" + mApn);
    615                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNSETTING, mApn);
    616                     break;
    617                 }
    618                 case DataConnectionAc.REQ_GET_LINK_PROPERTIES: {
    619                     LinkProperties lp = new LinkProperties(mLinkProperties);
    620                     if (VDBG) log("REQ_GET_LINK_PROPERTIES linkProperties" + lp);
    621                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_PROPERTIES, lp);
    622                     break;
    623                 }
    624                 case DataConnectionAc.REQ_SET_LINK_PROPERTIES_HTTP_PROXY: {
    625                     ProxyProperties proxy = (ProxyProperties) msg.obj;
    626                     if (VDBG) log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy);
    627                     mLinkProperties.setHttpProxy(proxy);
    628                     mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_LINK_PROPERTIES_HTTP_PROXY);
    629                     break;
    630                 }
    631                 case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: {
    632                     DataCallState newState = (DataCallState) msg.obj;
    633                     UpdateLinkPropertyResult result =
    634                                              updateLinkProperty(newState);
    635                     if (VDBG) {
    636                         log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE result="
    637                             + result + " newState=" + newState);
    638                     }
    639                     mAc.replyToMessage(msg,
    640                                    DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE,
    641                                    result);
    642                     break;
    643                 }
    644                 case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: {
    645                     LinkCapabilities lc = new LinkCapabilities(mCapabilities);
    646                     if (VDBG) log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
    647                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_CAPABILITIES, lc);
    648                     break;
    649                 }
    650                 case DataConnectionAc.REQ_RESET:
    651                     if (VDBG) log("DcDefaultState: msg.what=REQ_RESET");
    652                     mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
    653                     transitionTo(mInactiveState);
    654                     break;
    655                 case DataConnectionAc.REQ_GET_REFCOUNT: {
    656                     if (VDBG) log("REQ_GET_REFCOUNT  refCount=" + mRefCount);
    657                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount);
    658                     break;
    659                 }
    660                 case DataConnectionAc.REQ_ADD_APNCONTEXT: {
    661                     ApnContext apnContext = (ApnContext) msg.obj;
    662                     if (VDBG) log("REQ_ADD_APNCONTEXT apn=" + apnContext.getApnType());
    663                     if (!mApnList.contains(apnContext)) {
    664                         mApnList.add(apnContext);
    665                     }
    666                     mAc.replyToMessage(msg, DataConnectionAc.RSP_ADD_APNCONTEXT);
    667                     break;
    668                 }
    669                 case DataConnectionAc.REQ_REMOVE_APNCONTEXT: {
    670                     ApnContext apnContext = (ApnContext) msg.obj;
    671                     if (VDBG) log("REQ_REMOVE_APNCONTEXT apn=" + apnContext.getApnType());
    672                     mApnList.remove(apnContext);
    673                     mAc.replyToMessage(msg, DataConnectionAc.RSP_REMOVE_APNCONTEXT);
    674                     break;
    675                 }
    676                 case DataConnectionAc.REQ_GET_APNCONTEXT_LIST: {
    677                     if (VDBG) log("REQ_GET_APNCONTEXT_LIST num in list=" + mApnList.size());
    678                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNCONTEXT_LIST,
    679                                        new ArrayList<ApnContext>(mApnList));
    680                     break;
    681                 }
    682                 case DataConnectionAc.REQ_SET_RECONNECT_INTENT: {
    683                     PendingIntent intent = (PendingIntent) msg.obj;
    684                     if (VDBG) log("REQ_SET_RECONNECT_INTENT");
    685                     mReconnectIntent = intent;
    686                     mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_RECONNECT_INTENT);
    687                     break;
    688                 }
    689                 case DataConnectionAc.REQ_GET_RECONNECT_INTENT: {
    690                     if (VDBG) log("REQ_GET_RECONNECT_INTENT");
    691                     mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_RECONNECT_INTENT,
    692                                        mReconnectIntent);
    693                     break;
    694                 }
    695                 case EVENT_CONNECT:
    696                     if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
    697                     ConnectionParams cp = (ConnectionParams) msg.obj;
    698                     notifyConnectCompleted(cp, FailCause.UNKNOWN);
    699                     break;
    700 
    701                 case EVENT_DISCONNECT:
    702                     if (DBG) log("DcDefaultState: msg.what=EVENT_DISCONNECT");
    703                     notifyDisconnectCompleted((DisconnectParams) msg.obj);
    704                     break;
    705 
    706                 case EVENT_RIL_CONNECTED:
    707                     ar = (AsyncResult)msg.obj;
    708                     if (ar.exception == null) {
    709                         mRilVersion = (Integer)ar.result;
    710                         if (DBG) {
    711                             log("DcDefaultState: msg.what=EVENT_RIL_CONNECTED mRilVersion=" +
    712                                 mRilVersion);
    713                         }
    714                     } else {
    715                         log("Unexpected exception on EVENT_RIL_CONNECTED");
    716                         mRilVersion = -1;
    717                     }
    718                     break;
    719 
    720                 default:
    721                     if (DBG) {
    722                         log("DcDefaultState: shouldn't happen but ignore msg.what=0x" +
    723                                 Integer.toHexString(msg.what));
    724                     }
    725                     break;
    726             }
    727 
    728             return HANDLED;
    729         }
    730     }
    731     private DcDefaultState mDefaultState = new DcDefaultState();
    732 
    733     /**
    734      * The state machine is inactive and expects a EVENT_CONNECT.
    735      */
    736     private class DcInactiveState extends State {
    737         private ConnectionParams mConnectionParams = null;
    738         private FailCause mFailCause = null;
    739         private DisconnectParams mDisconnectParams = null;
    740 
    741         public void setEnterNotificationParams(ConnectionParams cp, FailCause cause,
    742                                                int retryOverride) {
    743             if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
    744             mConnectionParams = cp;
    745             mFailCause = cause;
    746             mRetryOverride = retryOverride;
    747         }
    748 
    749         public void setEnterNotificationParams(DisconnectParams dp) {
    750             if (VDBG) log("DcInactiveState: setEnterNoticationParams dp");
    751             mDisconnectParams = dp;
    752         }
    753 
    754         @Override
    755         public void enter() {
    756             mTag += 1;
    757 
    758             /**
    759              * Now that we've transitioned to Inactive state we
    760              * can send notifications. Previously we sent the
    761              * notifications in the processMessage handler but
    762              * that caused a race condition because the synchronous
    763              * call to isInactive.
    764              */
    765             if ((mConnectionParams != null) && (mFailCause != null)) {
    766                 if (VDBG) log("DcInactiveState: enter notifyConnectCompleted");
    767                 notifyConnectCompleted(mConnectionParams, mFailCause);
    768             }
    769             if (mDisconnectParams != null) {
    770                 if (VDBG) log("DcInactiveState: enter notifyDisconnectCompleted");
    771                 notifyDisconnectCompleted(mDisconnectParams);
    772             }
    773             clearSettings();
    774         }
    775 
    776         @Override
    777         public void exit() {
    778             // clear notifications
    779             mConnectionParams = null;
    780             mFailCause = null;
    781             mDisconnectParams = null;
    782         }
    783 
    784         @Override
    785         public boolean processMessage(Message msg) {
    786             boolean retVal;
    787 
    788             switch (msg.what) {
    789                 case DataConnectionAc.REQ_RESET:
    790                     if (DBG) {
    791                         log("DcInactiveState: msg.what=RSP_RESET, ignore we're already reset");
    792                     }
    793                     mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
    794                     retVal = HANDLED;
    795                     break;
    796 
    797                 case EVENT_CONNECT:
    798                     ConnectionParams cp = (ConnectionParams) msg.obj;
    799                     cp.tag = mTag;
    800                     if (DBG) {
    801                         log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = "
    802                                 + mRefCount);
    803                     }
    804                     mRefCount = 1;
    805                     onConnect(cp);
    806                     transitionTo(mActivatingState);
    807                     retVal = HANDLED;
    808                     break;
    809 
    810                 default:
    811                     if (VDBG) {
    812                         log("DcInactiveState nothandled msg.what=0x" +
    813                                 Integer.toHexString(msg.what));
    814                     }
    815                     retVal = NOT_HANDLED;
    816                     break;
    817             }
    818             return retVal;
    819         }
    820     }
    821     private DcInactiveState mInactiveState = new DcInactiveState();
    822 
    823     /**
    824      * The state machine is activating a connection.
    825      */
    826     private class DcActivatingState extends State {
    827         @Override
    828         public boolean processMessage(Message msg) {
    829             boolean retVal;
    830             AsyncResult ar;
    831             ConnectionParams cp;
    832 
    833             switch (msg.what) {
    834                 case EVENT_DISCONNECT:
    835                     if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT"
    836                             + mRefCount);
    837                     deferMessage(msg);
    838                     retVal = HANDLED;
    839                     break;
    840 
    841                 case EVENT_CONNECT:
    842                     if (DBG) log("DcActivatingState deferring msg.what=EVENT_CONNECT refCount = "
    843                             + mRefCount);
    844                     deferMessage(msg);
    845                     retVal = HANDLED;
    846                     break;
    847 
    848                 case EVENT_SETUP_DATA_CONNECTION_DONE:
    849                     if (DBG) log("DcActivatingState msg.what=EVENT_SETUP_DATA_CONNECTION_DONE");
    850 
    851                     ar = (AsyncResult) msg.obj;
    852                     cp = (ConnectionParams) ar.userObj;
    853 
    854                     DataCallState.SetupResult result = onSetupConnectionCompleted(ar);
    855                     if (DBG) log("DcActivatingState onSetupConnectionCompleted result=" + result);
    856                     switch (result) {
    857                         case SUCCESS:
    858                             // All is well
    859                             mActiveState.setEnterNotificationParams(cp, FailCause.NONE);
    860                             transitionTo(mActiveState);
    861                             break;
    862                         case ERR_BadCommand:
    863                             // Vendor ril rejected the command and didn't connect.
    864                             // Transition to inactive but send notifications after
    865                             // we've entered the mInactive state.
    866                             mInactiveState.setEnterNotificationParams(cp, result.mFailCause, -1);
    867                             transitionTo(mInactiveState);
    868                             break;
    869                         case ERR_UnacceptableParameter:
    870                             // The addresses given from the RIL are bad
    871                             tearDownData(cp);
    872                             transitionTo(mDisconnectingErrorCreatingConnection);
    873                             break;
    874                         case ERR_GetLastErrorFromRil:
    875                             // Request failed and this is an old RIL
    876                             phone.mCM.getLastDataCallFailCause(
    877                                     obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp));
    878                             break;
    879                         case ERR_RilError:
    880                             // Request failed and mFailCause has the reason
    881                             mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
    882                                                                       getSuggestedRetryTime(ar));
    883                             transitionTo(mInactiveState);
    884                             break;
    885                         case ERR_Stale:
    886                             // Request is stale, ignore.
    887                             break;
    888                         default:
    889                             throw new RuntimeException("Unknown SetupResult, should not happen");
    890                     }
    891                     retVal = HANDLED;
    892                     break;
    893 
    894                 case EVENT_GET_LAST_FAIL_DONE:
    895                     ar = (AsyncResult) msg.obj;
    896                     cp = (ConnectionParams) ar.userObj;
    897                     FailCause cause = FailCause.UNKNOWN;
    898 
    899                     if (cp.tag == mTag) {
    900                         if (DBG) log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE");
    901                         if (ar.exception == null) {
    902                             int rilFailCause = ((int[]) (ar.result))[0];
    903                             cause = FailCause.fromInt(rilFailCause);
    904                         }
    905                         // Transition to inactive but send notifications after
    906                         // we've entered the mInactive state.
    907                         mInactiveState.setEnterNotificationParams(cp, cause, -1);
    908                         transitionTo(mInactiveState);
    909                     } else {
    910                         if (DBG) {
    911                             log("DcActivatingState EVENT_GET_LAST_FAIL_DONE is stale cp.tag="
    912                                 + cp.tag + ", mTag=" + mTag);
    913                         }
    914                     }
    915 
    916                     retVal = HANDLED;
    917                     break;
    918 
    919                 default:
    920                     if (VDBG) {
    921                         log("DcActivatingState not handled msg.what=0x" +
    922                                 Integer.toHexString(msg.what));
    923                     }
    924                     retVal = NOT_HANDLED;
    925                     break;
    926             }
    927             return retVal;
    928         }
    929     }
    930     private DcActivatingState mActivatingState = new DcActivatingState();
    931 
    932     /**
    933      * The state machine is connected, expecting an EVENT_DISCONNECT.
    934      */
    935     private class DcActiveState extends State {
    936         private ConnectionParams mConnectionParams = null;
    937         private FailCause mFailCause = null;
    938 
    939         public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
    940             if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
    941             mConnectionParams = cp;
    942             mFailCause = cause;
    943         }
    944 
    945         @Override public void enter() {
    946             /**
    947              * Now that we've transitioned to Active state we
    948              * can send notifications. Previously we sent the
    949              * notifications in the processMessage handler but
    950              * that caused a race condition because the synchronous
    951              * call to isActive.
    952              */
    953             if ((mConnectionParams != null) && (mFailCause != null)) {
    954                 if (VDBG) log("DcActiveState: enter notifyConnectCompleted");
    955                 notifyConnectCompleted(mConnectionParams, mFailCause);
    956             }
    957         }
    958 
    959         @Override
    960         public void exit() {
    961             // clear notifications
    962             mConnectionParams = null;
    963             mFailCause = null;
    964         }
    965 
    966         @Override
    967         public boolean processMessage(Message msg) {
    968             boolean retVal;
    969 
    970             switch (msg.what) {
    971                 case EVENT_CONNECT:
    972                     mRefCount++;
    973                     if (DBG) log("DcActiveState msg.what=EVENT_CONNECT RefCount=" + mRefCount);
    974                     if (msg.obj != null) {
    975                         notifyConnectCompleted((ConnectionParams) msg.obj, FailCause.NONE);
    976                     }
    977                     retVal = HANDLED;
    978                     break;
    979                 case EVENT_DISCONNECT:
    980                     mRefCount--;
    981                     if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" + mRefCount);
    982                     if (mRefCount == 0)
    983                     {
    984                         DisconnectParams dp = (DisconnectParams) msg.obj;
    985                         dp.tag = mTag;
    986                         tearDownData(dp);
    987                         transitionTo(mDisconnectingState);
    988                     } else {
    989                         if (msg.obj != null) {
    990                             notifyDisconnectCompleted((DisconnectParams) msg.obj);
    991                         }
    992                     }
    993                     retVal = HANDLED;
    994                     break;
    995 
    996                 default:
    997                     if (VDBG) {
    998                         log("DcActiveState not handled msg.what=0x" +
    999                                 Integer.toHexString(msg.what));
   1000                     }
   1001                     retVal = NOT_HANDLED;
   1002                     break;
   1003             }
   1004             return retVal;
   1005         }
   1006     }
   1007     private DcActiveState mActiveState = new DcActiveState();
   1008 
   1009     /**
   1010      * The state machine is disconnecting.
   1011      */
   1012     private class DcDisconnectingState extends State {
   1013         @Override
   1014         public boolean processMessage(Message msg) {
   1015             boolean retVal;
   1016 
   1017             switch (msg.what) {
   1018                 case EVENT_CONNECT:
   1019                     if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
   1020                             + mRefCount);
   1021                     deferMessage(msg);
   1022                     retVal = HANDLED;
   1023                     break;
   1024 
   1025                 case EVENT_DEACTIVATE_DONE:
   1026                     if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE");
   1027                     AsyncResult ar = (AsyncResult) msg.obj;
   1028                     DisconnectParams dp = (DisconnectParams) ar.userObj;
   1029                     if (dp.tag == mTag) {
   1030                         // Transition to inactive but send notifications after
   1031                         // we've entered the mInactive state.
   1032                         mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
   1033                         transitionTo(mInactiveState);
   1034                     } else {
   1035                         if (DBG) log("DcDisconnectState EVENT_DEACTIVATE_DONE stale dp.tag="
   1036                                 + dp.tag + " mTag=" + mTag);
   1037                     }
   1038                     retVal = HANDLED;
   1039                     break;
   1040 
   1041                 default:
   1042                     if (VDBG) {
   1043                         log("DcDisconnectingState not handled msg.what=0x" +
   1044                                 Integer.toHexString(msg.what));
   1045                     }
   1046                     retVal = NOT_HANDLED;
   1047                     break;
   1048             }
   1049             return retVal;
   1050         }
   1051     }
   1052     private DcDisconnectingState mDisconnectingState = new DcDisconnectingState();
   1053 
   1054     /**
   1055      * The state machine is disconnecting after an creating a connection.
   1056      */
   1057     private class DcDisconnectionErrorCreatingConnection extends State {
   1058         @Override
   1059         public boolean processMessage(Message msg) {
   1060             boolean retVal;
   1061 
   1062             switch (msg.what) {
   1063                 case EVENT_DEACTIVATE_DONE:
   1064                     AsyncResult ar = (AsyncResult) msg.obj;
   1065                     ConnectionParams cp = (ConnectionParams) ar.userObj;
   1066                     if (cp.tag == mTag) {
   1067                         if (DBG) {
   1068                             log("DcDisconnectionErrorCreatingConnection" +
   1069                                 " msg.what=EVENT_DEACTIVATE_DONE");
   1070                         }
   1071 
   1072                         // Transition to inactive but send notifications after
   1073                         // we've entered the mInactive state.
   1074                         mInactiveState.setEnterNotificationParams(cp,
   1075                                 FailCause.UNACCEPTABLE_NETWORK_PARAMETER, -1);
   1076                         transitionTo(mInactiveState);
   1077                     } else {
   1078                         if (DBG) {
   1079                             log("DcDisconnectionErrorCreatingConnection EVENT_DEACTIVATE_DONE" +
   1080                                     " stale dp.tag=" + cp.tag + ", mTag=" + mTag);
   1081                         }
   1082                     }
   1083                     retVal = HANDLED;
   1084                     break;
   1085 
   1086                 default:
   1087                     if (VDBG) {
   1088                         log("DcDisconnectionErrorCreatingConnection not handled msg.what=0x"
   1089                                 + Integer.toHexString(msg.what));
   1090                     }
   1091                     retVal = NOT_HANDLED;
   1092                     break;
   1093             }
   1094             return retVal;
   1095         }
   1096     }
   1097     private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection =
   1098                 new DcDisconnectionErrorCreatingConnection();
   1099 
   1100     // ******* public interface
   1101 
   1102     /**
   1103      * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
   1104      * Used for cellular networks that use Acesss Point Names (APN) such
   1105      * as GSM networks.
   1106      *
   1107      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
   1108      *        With AsyncResult.userObj set to the original msg.obj,
   1109      *        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
   1110      * @param apn is the Access Point Name to bring up a connection to
   1111      */
   1112     public void bringUp(Message onCompletedMsg, ApnSetting apn) {
   1113         sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
   1114     }
   1115 
   1116     /**
   1117      * Tear down the connection through the apn on the network.
   1118      *
   1119      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
   1120      *        With AsyncResult.userObj set to the original msg.obj.
   1121      */
   1122     public void tearDown(String reason, Message onCompletedMsg) {
   1123         sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(reason, onCompletedMsg)));
   1124     }
   1125 }
   1126