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 import com.android.internal.telephony.gsm.ApnSetting;
     20 
     21 import com.android.internal.util.HierarchicalState;
     22 import com.android.internal.util.HierarchicalStateMachine;
     23 
     24 import android.os.AsyncResult;
     25 import android.os.Message;
     26 import android.os.SystemProperties;
     27 import android.util.EventLog;
     28 
     29 /**
     30  * {@hide}
     31  *
     32  * DataConnection HierarchicalStateMachine.
     33  *
     34  * This is an abstract base class for representing a single data connection.
     35  * Instances of this class such as <code>CdmaDataConnection</code> and
     36  * <code>GsmDataConnection</code>, * represent a connection via the cellular network.
     37  * There may be multiple data connections and all of them are managed by the
     38  * <code>DataConnectionTracker</code>.
     39  *
     40  * Instances are asynchronous state machines and have two primary entry points
     41  * <code>connect()</code> and <code>disconnect</code>. The message a parameter will be returned
     42  * hen the operation completes. The <code>msg.obj</code> will contain an AsyncResult
     43  * object and <code>AsyncResult.userObj</code> is the original <code>msg.obj</code>. if successful
     44  * with the <code>AsyncResult.result == null</code> and <code>AsyncResult.exception == null</code>.
     45  * If an error <code>AsyncResult.result = FailCause</code> and
     46  * <code>AsyncResult.exception = new Exception()</code>.
     47  *
     48  * The other public methods are provided for debugging.
     49  *
     50  * Below is the state machine description for this class.
     51  *
     52  * DataConnection {
     53  *   + mDefaultState {
     54  *        EVENT_RESET { clearSettings, notifiyDisconnectCompleted, >mInactiveState }.
     55  *        EVENT_CONNECT {  notifyConnectCompleted(FailCause.UNKNOWN) }.
     56  *        EVENT_DISCONNECT { notifyDisconnectCompleted }.
     57  *
     58  *        // Ignored messages
     59  *        EVENT_SETUP_DATA_CONNECTION_DONE,
     60  *        EVENT_GET_LAST_FAIL_DONE,
     61  *        EVENT_DEACTIVATE_DONE.
     62  *     }
     63  *   ++ # mInactiveState
     64  *        e(doNotifications)
     65  *        x(clearNotifications) {
     66  *            EVENT_RESET { notifiyDisconnectCompleted }.
     67  *            EVENT_CONNECT {startConnecting, >mActivatingState }.
     68  *        }
     69  *   ++   mActivatingState {
     70  *            EVENT_DISCONNECT { %EVENT_DISCONNECT }.
     71  *            EVENT_SETUP_DATA_CONNECTION_DONE {
     72  *                  if (SUCCESS) { notifyConnectCompleted(FailCause.NONE), >mActiveState }.
     73  *                  if (ERR_BadCommand) {
     74  *                         notifyConnectCompleted(FailCause.UNKNOWN), >mInactiveState }.
     75  *                  if (ERR_BadDns) { tearDownData($DEACTIVATE_DONE), >mDisconnectingBadDnsState }.
     76  *                  if (ERR_Other) { getLastDataCallFailCause($EVENT_GET_LAST_FAIL_DONE) }.
     77  *                  if (ERR_Stale) {}.
     78  *            }
     79  *            EVENT_GET_LAST_FAIL_DONE { notifyConnectCompleted(result), >mInactive }.
     80  *        }
     81  *   ++   mActiveState {
     82  *            EVENT_DISCONNECT { tearDownData($EVENT_DEACTIVATE_DONE), >mDisconnecting }.
     83  *        }
     84  *   ++   mDisconnectingState {
     85  *            EVENT_DEACTIVATE_DONE { notifyDisconnectCompleted, >mInactiveState }.
     86  *        }
     87  *   ++   mDisconnectingBadDnsState {
     88  *            EVENT_DEACTIVATE_DONE { notifyConnectComplete(FailCause.UNKNOWN), >mInactiveState }.
     89  *        }
     90  *  }
     91  */
     92 public abstract class DataConnection extends HierarchicalStateMachine {
     93     protected static final boolean DBG = true;
     94 
     95     protected static Object mCountLock = new Object();
     96     protected static int mCount;
     97 
     98     /**
     99      * Class returned by onSetupConnectionCompleted.
    100      */
    101     protected enum SetupResult {
    102         ERR_BadCommand,
    103         ERR_BadDns,
    104         ERR_Other,
    105         ERR_Stale,
    106         SUCCESS;
    107 
    108         public FailCause mFailCause;
    109 
    110         @Override
    111         public String toString() {
    112             switch (this) {
    113                 case ERR_BadCommand: return "Bad Command";
    114                 case ERR_BadDns: return "Bad DNS";
    115                 case ERR_Other: return "Other error";
    116                 case ERR_Stale: return "Stale command";
    117                 case SUCCESS: return "SUCCESS";
    118                 default: return "unknown";
    119             }
    120         }
    121     }
    122 
    123     /**
    124      * Used internally for saving connecting parameters.
    125      */
    126     protected static class ConnectionParams {
    127         public ConnectionParams(ApnSetting apn, Message onCompletedMsg) {
    128             this.apn = apn;
    129             this.onCompletedMsg = onCompletedMsg;
    130         }
    131 
    132         public int tag;
    133         public ApnSetting apn;
    134         public Message onCompletedMsg;
    135     }
    136 
    137     /**
    138      * An instance used for notification of blockingReset.
    139      * TODO: Remove when blockingReset is removed.
    140      */
    141     class ResetSynchronouslyLock {
    142     }
    143 
    144     /**
    145      * Used internally for saving disconnecting parameters.
    146      */
    147     protected static class DisconnectParams {
    148         public DisconnectParams(Message onCompletedMsg) {
    149             this.onCompletedMsg = onCompletedMsg;
    150         }
    151         public DisconnectParams(ResetSynchronouslyLock lockObj) {
    152             this.lockObj = lockObj;
    153         }
    154 
    155         public int tag;
    156         public Message onCompletedMsg;
    157         public ResetSynchronouslyLock lockObj;
    158     }
    159 
    160     /**
    161      * Returned as the reason for a connection failure.
    162      */
    163     public enum FailCause {
    164         NONE,
    165         OPERATOR_BARRED,
    166         INSUFFICIENT_RESOURCES,
    167         MISSING_UNKNOWN_APN,
    168         UNKNOWN_PDP_ADDRESS,
    169         USER_AUTHENTICATION,
    170         ACTIVATION_REJECT_GGSN,
    171         ACTIVATION_REJECT_UNSPECIFIED,
    172         SERVICE_OPTION_NOT_SUPPORTED,
    173         SERVICE_OPTION_NOT_SUBSCRIBED,
    174         SERVICE_OPTION_OUT_OF_ORDER,
    175         NSAPI_IN_USE,
    176         PROTOCOL_ERRORS,
    177         REGISTRATION_FAIL,
    178         GPRS_REGISTRATION_FAIL,
    179         UNKNOWN,
    180 
    181         RADIO_NOT_AVAILABLE;
    182 
    183         public boolean isPermanentFail() {
    184             return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) ||
    185                    (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) ||
    186                    (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
    187                    (this == SERVICE_OPTION_NOT_SUPPORTED) ||
    188                    (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) ||
    189                    (this == PROTOCOL_ERRORS);
    190         }
    191 
    192         public boolean isEventLoggable() {
    193             return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) ||
    194                     (this == UNKNOWN_PDP_ADDRESS) || (this == USER_AUTHENTICATION) ||
    195                     (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
    196                     (this == SERVICE_OPTION_NOT_SUBSCRIBED) ||
    197                     (this == SERVICE_OPTION_NOT_SUPPORTED) ||
    198                     (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) ||
    199                     (this == PROTOCOL_ERRORS);
    200         }
    201 
    202         @Override
    203         public String toString() {
    204             switch (this) {
    205             case NONE:
    206                 return "No Error";
    207             case OPERATOR_BARRED:
    208                 return "Operator Barred";
    209             case INSUFFICIENT_RESOURCES:
    210                 return "Insufficient Resources";
    211             case MISSING_UNKNOWN_APN:
    212                 return "Missing / Unknown APN";
    213             case UNKNOWN_PDP_ADDRESS:
    214                 return "Unknown PDP Address";
    215             case USER_AUTHENTICATION:
    216                 return "Error User Authentication";
    217             case ACTIVATION_REJECT_GGSN:
    218                 return "Activation Reject GGSN";
    219             case ACTIVATION_REJECT_UNSPECIFIED:
    220                 return "Activation Reject unspecified";
    221             case SERVICE_OPTION_NOT_SUPPORTED:
    222                 return "Data Not Supported";
    223             case SERVICE_OPTION_NOT_SUBSCRIBED:
    224                 return "Data Not subscribed";
    225             case SERVICE_OPTION_OUT_OF_ORDER:
    226                 return "Data Services Out of Order";
    227             case NSAPI_IN_USE:
    228                 return "NSAPI in use";
    229             case PROTOCOL_ERRORS:
    230                 return "Protocol Errors";
    231             case REGISTRATION_FAIL:
    232                 return "Network Registration Failure";
    233             case GPRS_REGISTRATION_FAIL:
    234                 return "Data Network Registration Failure";
    235             case RADIO_NOT_AVAILABLE:
    236                 return "Radio Not Available";
    237             default:
    238                 return "Unknown Data Error";
    239             }
    240         }
    241     }
    242 
    243     // ***** Event codes for driving the state machine
    244     protected static final int EVENT_RESET = 1;
    245     protected static final int EVENT_CONNECT = 2;
    246     protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = 3;
    247     protected static final int EVENT_GET_LAST_FAIL_DONE = 4;
    248     protected static final int EVENT_DEACTIVATE_DONE = 5;
    249     protected static final int EVENT_DISCONNECT = 6;
    250 
    251     //***** Tag IDs for EventLog
    252     protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100;
    253 
    254     //***** Member Variables
    255     protected int mTag;
    256     protected PhoneBase phone;
    257     protected int cid;
    258     protected String interfaceName;
    259     protected String ipAddress;
    260     protected String gatewayAddress;
    261     protected String[] dnsServers;
    262     protected long createTime;
    263     protected long lastFailTime;
    264     protected FailCause lastFailCause;
    265     protected static final String NULL_IP = "0.0.0.0";
    266     Object userData;
    267 
    268     //***** Abstract methods
    269     public abstract String toString();
    270 
    271     protected abstract void onConnect(ConnectionParams cp);
    272 
    273     protected abstract FailCause getFailCauseFromRequest(int rilCause);
    274 
    275     protected abstract boolean isDnsOk(String[] domainNameServers);
    276 
    277     protected abstract void log(String s);
    278 
    279 
    280    //***** Constructor
    281     protected DataConnection(PhoneBase phone, String name) {
    282         super(name);
    283         if (DBG) log("DataConnection constructor E");
    284         this.phone = phone;
    285         this.cid = -1;
    286         this.dnsServers = new String[2];
    287 
    288         clearSettings();
    289 
    290         setDbg(false);
    291         addState(mDefaultState);
    292             addState(mInactiveState, mDefaultState);
    293             addState(mActivatingState, mDefaultState);
    294             addState(mActiveState, mDefaultState);
    295             addState(mDisconnectingState, mDefaultState);
    296             addState(mDisconnectingBadDnsState, mDefaultState);
    297         setInitialState(mInactiveState);
    298         if (DBG) log("DataConnection constructor X");
    299     }
    300 
    301     /**
    302      * TearDown the data connection.
    303      *
    304      * @param o will be returned in AsyncResult.userObj
    305      *          and is either a DisconnectParams or ConnectionParams.
    306      */
    307     private void tearDownData(Object o) {
    308         if (phone.mCM.getRadioState().isOn()) {
    309             if (DBG) log("tearDownData radio is on, call deactivateDataCall");
    310             phone.mCM.deactivateDataCall(cid, obtainMessage(EVENT_DEACTIVATE_DONE, o));
    311         } else {
    312             if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately");
    313             AsyncResult ar = new AsyncResult(o, null, null);
    314             sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, ar));
    315         }
    316     }
    317 
    318     /**
    319      * Send the connectionCompletedMsg.
    320      *
    321      * @param cp is the ConnectionParams
    322      * @param cause
    323      */
    324     private void notifyConnectCompleted(ConnectionParams cp, FailCause cause) {
    325         Message connectionCompletedMsg = cp.onCompletedMsg;
    326         if (connectionCompletedMsg == null) {
    327             return;
    328         }
    329 
    330         long timeStamp = System.currentTimeMillis();
    331         connectionCompletedMsg.arg1 = cid;
    332 
    333         if (cause == FailCause.NONE) {
    334             createTime = timeStamp;
    335             AsyncResult.forMessage(connectionCompletedMsg);
    336         } else {
    337             lastFailCause = cause;
    338             lastFailTime = timeStamp;
    339             AsyncResult.forMessage(connectionCompletedMsg, cause, new Exception());
    340         }
    341         if (DBG) log("notifyConnection at " + timeStamp + " cause=" + cause);
    342 
    343         connectionCompletedMsg.sendToTarget();
    344     }
    345 
    346     /**
    347      * Send ar.userObj if its a message, which is should be back to originator.
    348      *
    349      * @param dp is the DisconnectParams.
    350      */
    351     private void notifyDisconnectCompleted(DisconnectParams dp) {
    352         if (DBG) log("NotifyDisconnectCompleted");
    353 
    354         if (dp.onCompletedMsg != null) {
    355             Message msg = dp.onCompletedMsg;
    356             log(String.format("msg.what=%d msg.obj=%s",
    357                     msg.what, ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
    358             AsyncResult.forMessage(msg);
    359             msg.sendToTarget();
    360         }
    361         if (dp.lockObj != null) {
    362             synchronized(dp.lockObj) {
    363                 dp.lockObj.notify();
    364             }
    365         }
    366 
    367         clearSettings();
    368     }
    369 
    370     /**
    371      * Clear all settings called when entering mInactiveState.
    372      */
    373     protected void clearSettings() {
    374         if (DBG) log("clearSettings");
    375 
    376         this.createTime = -1;
    377         this.lastFailTime = -1;
    378         this.lastFailCause = FailCause.NONE;
    379 
    380         interfaceName = null;
    381         ipAddress = null;
    382         gatewayAddress = null;
    383         dnsServers[0] = null;
    384         dnsServers[1] = null;
    385     }
    386 
    387     /**
    388      * Process setup completion.
    389      *
    390      * @param ar is the result
    391      * @return SetupResult.
    392      */
    393     private SetupResult onSetupConnectionCompleted(AsyncResult ar) {
    394         SetupResult result;
    395         String[] response = ((String[]) ar.result);
    396         ConnectionParams cp = (ConnectionParams) ar.userObj;
    397 
    398         if (ar.exception != null) {
    399             if (DBG) log("DataConnection Init failed " + ar.exception);
    400 
    401             if (ar.exception instanceof CommandException
    402                     && ((CommandException) (ar.exception)).getCommandError()
    403                     == CommandException.Error.RADIO_NOT_AVAILABLE) {
    404                 result = SetupResult.ERR_BadCommand;
    405                 result.mFailCause = FailCause.RADIO_NOT_AVAILABLE;
    406             } else {
    407                 result = SetupResult.ERR_Other;
    408             }
    409         } else if (cp.tag != mTag) {
    410             if (DBG) {
    411                 log("BUG: onSetupConnectionCompleted is stale cp.tag=" + cp.tag + ", mtag=" + mTag);
    412             }
    413             result = SetupResult.ERR_Stale;
    414         } else {
    415 //            log("onSetupConnectionCompleted received " + response.length + " response strings:");
    416 //            for (int i = 0; i < response.length; i++) {
    417 //                log("  response[" + i + "]='" + response[i] + "'");
    418 //            }
    419             if (response.length >= 2) {
    420                 cid = Integer.parseInt(response[0]);
    421                 interfaceName = response[1];
    422                 if (response.length > 2) {
    423                     ipAddress = response[2];
    424                     String prefix = "net." + interfaceName + ".";
    425                     gatewayAddress = SystemProperties.get(prefix + "gw");
    426                     dnsServers[0] = SystemProperties.get(prefix + "dns1");
    427                     dnsServers[1] = SystemProperties.get(prefix + "dns2");
    428                     if (DBG) {
    429                         log("interface=" + interfaceName + " ipAddress=" + ipAddress
    430                             + " gateway=" + gatewayAddress + " DNS1=" + dnsServers[0]
    431                             + " DNS2=" + dnsServers[1]);
    432                     }
    433 
    434                     if (isDnsOk(dnsServers)) {
    435                         result = SetupResult.SUCCESS;
    436                     } else {
    437                         result = SetupResult.ERR_BadDns;
    438                     }
    439                 } else {
    440                     result = SetupResult.SUCCESS;
    441                 }
    442             } else {
    443                 result = SetupResult.ERR_Other;
    444             }
    445         }
    446 
    447         if (DBG) log("DataConnection setup result='" + result + "' on cid=" + cid);
    448         return result;
    449     }
    450 
    451     /**
    452      * The parent state for all other states.
    453      */
    454     private class DcDefaultState extends HierarchicalState {
    455         @Override
    456         protected boolean processMessage(Message msg) {
    457             AsyncResult ar;
    458 
    459             switch (msg.what) {
    460                 case EVENT_RESET:
    461                     if (DBG) log("DcDefaultState: msg.what=EVENT_RESET");
    462                     clearSettings();
    463                     if (msg.obj != null) {
    464                         notifyDisconnectCompleted((DisconnectParams) msg.obj);
    465                     }
    466                     transitionTo(mInactiveState);
    467                     break;
    468 
    469                 case EVENT_CONNECT:
    470                     if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
    471                     ConnectionParams cp = (ConnectionParams) msg.obj;
    472                     notifyConnectCompleted(cp, FailCause.UNKNOWN);
    473                     break;
    474 
    475                 case EVENT_DISCONNECT:
    476                     if (DBG) log("DcDefaultState: msg.what=EVENT_DISCONNECT");
    477                     notifyDisconnectCompleted((DisconnectParams) msg.obj);
    478                     break;
    479 
    480                 default:
    481                     if (DBG) {
    482                         log("DcDefaultState: shouldn't happen but ignore msg.what=" + msg.what);
    483                     }
    484                     break;
    485             }
    486 
    487             return true;
    488         }
    489     }
    490     private DcDefaultState mDefaultState = new DcDefaultState();
    491 
    492     /**
    493      * The state machine is inactive and expects a EVENT_CONNECT.
    494      */
    495     private class DcInactiveState extends HierarchicalState {
    496         private ConnectionParams mConnectionParams = null;
    497         private FailCause mFailCause = null;
    498         private DisconnectParams mDisconnectParams = null;
    499 
    500         public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
    501             log("DcInactiveState: setEnterNoticationParams cp,cause");
    502             mConnectionParams = cp;
    503             mFailCause = cause;
    504         }
    505 
    506         public void setEnterNotificationParams(DisconnectParams dp) {
    507           log("DcInactiveState: setEnterNoticationParams dp");
    508             mDisconnectParams = dp;
    509         }
    510 
    511         @Override protected void enter() {
    512             mTag += 1;
    513 
    514             /**
    515              * Now that we've transitioned to Inactive state we
    516              * can send notifications. Previously we sent the
    517              * notifications in the processMessage handler but
    518              * that caused a race condition because the synchronous
    519              * call to isInactive.
    520              */
    521             if ((mConnectionParams != null) && (mFailCause != null)) {
    522                 log("DcInactiveState: enter notifyConnectCompleted");
    523                 notifyConnectCompleted(mConnectionParams, mFailCause);
    524             }
    525             if (mDisconnectParams != null) {
    526               log("DcInactiveState: enter notifyDisconnectCompleted");
    527                 notifyDisconnectCompleted(mDisconnectParams);
    528             }
    529         }
    530 
    531         @Override protected void exit() {
    532             // clear notifications
    533             mConnectionParams = null;
    534             mFailCause = null;
    535             mDisconnectParams = null;
    536         }
    537 
    538         @Override protected boolean processMessage(Message msg) {
    539             boolean retVal;
    540 
    541             switch (msg.what) {
    542                 case EVENT_RESET:
    543                     if (DBG) {
    544                         log("DcInactiveState: msg.what=EVENT_RESET, ignore we're already reset");
    545                     }
    546                     if (msg.obj != null) {
    547                         notifyDisconnectCompleted((DisconnectParams) msg.obj);
    548                     }
    549                     retVal = true;
    550                     break;
    551 
    552                 case EVENT_CONNECT:
    553                     if (DBG) log("DcInactiveState msg.what=EVENT_CONNECT");
    554                     ConnectionParams cp = (ConnectionParams) msg.obj;
    555                     cp.tag = mTag;
    556                     onConnect(cp);
    557                     transitionTo(mActivatingState);
    558                     retVal = true;
    559                     break;
    560 
    561                 default:
    562                     if (DBG) log("DcInactiveState nothandled msg.what=" + msg.what);
    563                     retVal = false;
    564                     break;
    565             }
    566             return retVal;
    567         }
    568     }
    569     private DcInactiveState mInactiveState = new DcInactiveState();
    570 
    571     /**
    572      * The state machine is activating a connection.
    573      */
    574     private class DcActivatingState extends HierarchicalState {
    575         @Override protected boolean processMessage(Message msg) {
    576             boolean retVal;
    577             AsyncResult ar;
    578             ConnectionParams cp;
    579 
    580             switch (msg.what) {
    581                 case EVENT_DISCONNECT:
    582                     if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT");
    583                     deferMessage(msg);
    584                     retVal = true;
    585                     break;
    586 
    587                 case EVENT_SETUP_DATA_CONNECTION_DONE:
    588                     if (DBG) log("DcActivatingState msg.what=EVENT_SETUP_DATA_CONNECTION_DONE");
    589 
    590                     ar = (AsyncResult) msg.obj;
    591                     cp = (ConnectionParams) ar.userObj;
    592 
    593                     SetupResult result = onSetupConnectionCompleted(ar);
    594                     switch (result) {
    595                         case SUCCESS:
    596                             // All is well
    597                             mActiveState.setEnterNotificationParams(cp, FailCause.NONE);
    598                             transitionTo(mActiveState);
    599                             break;
    600                         case ERR_BadCommand:
    601                             // Vendor ril rejected the command and didn't connect.
    602                             // Transition to inactive but send notifications after
    603                             // we've entered the mInactive state.
    604                             mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
    605                             transitionTo(mInactiveState);
    606                             break;
    607                         case ERR_BadDns:
    608                             // Connection succeeded but DNS info is bad so disconnect
    609                             EventLog.writeEvent(EventLogTags.PDP_BAD_DNS_ADDRESS, dnsServers[0]);
    610                             tearDownData(cp);
    611                             transitionTo(mDisconnectingBadDnsState);
    612                             break;
    613                         case ERR_Other:
    614                             // Request the failure cause and process in this state
    615                             phone.mCM.getLastDataCallFailCause(
    616                                     obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp));
    617                             break;
    618                         case ERR_Stale:
    619                             // Request is stale, ignore.
    620                             break;
    621                         default:
    622                             throw new RuntimeException("Unkown SetupResult, should not happen");
    623                     }
    624                     retVal = true;
    625                     break;
    626 
    627                 case EVENT_GET_LAST_FAIL_DONE:
    628                     ar = (AsyncResult) msg.obj;
    629                     cp = (ConnectionParams) ar.userObj;
    630                     FailCause cause = FailCause.UNKNOWN;
    631 
    632                     if (cp.tag == mTag) {
    633                         if (DBG) log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE");
    634                         if (ar.exception == null) {
    635                             int rilFailCause = ((int[]) (ar.result))[0];
    636                             cause = getFailCauseFromRequest(rilFailCause);
    637                         }
    638                         // Transition to inactive but send notifications after
    639                         // we've entered the mInactive state.
    640                          mInactiveState.setEnterNotificationParams(cp, cause);
    641                          transitionTo(mInactiveState);
    642                     } else {
    643                         if (DBG) {
    644                             log("DcActivatingState EVENT_GET_LAST_FAIL_DONE is stale cp.tag="
    645                                 + cp.tag + ", mTag=" + mTag);
    646                         }
    647                     }
    648 
    649                     retVal = true;
    650                     break;
    651 
    652                 default:
    653                     if (DBG) log("DcActivatingState not handled msg.what=" + msg.what);
    654                     retVal = false;
    655                     break;
    656             }
    657             return retVal;
    658         }
    659     }
    660     private DcActivatingState mActivatingState = new DcActivatingState();
    661 
    662     /**
    663      * The state machine is connected, expecting an EVENT_DISCONNECT.
    664      */
    665     private class DcActiveState extends HierarchicalState {
    666         private ConnectionParams mConnectionParams = null;
    667         private FailCause mFailCause = null;
    668 
    669         public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
    670             log("DcInactiveState: setEnterNoticationParams cp,cause");
    671             mConnectionParams = cp;
    672             mFailCause = cause;
    673         }
    674 
    675         @Override public void enter() {
    676             /**
    677              * Now that we've transitioned to Active state we
    678              * can send notifications. Previously we sent the
    679              * notifications in the processMessage handler but
    680              * that caused a race condition because the synchronous
    681              * call to isActive.
    682              */
    683             if ((mConnectionParams != null) && (mFailCause != null)) {
    684                 log("DcActiveState: enter notifyConnectCompleted");
    685                 notifyConnectCompleted(mConnectionParams, mFailCause);
    686             }
    687         }
    688 
    689         @Override protected void exit() {
    690             // clear notifications
    691             mConnectionParams = null;
    692             mFailCause = null;
    693         }
    694 
    695         @Override protected boolean processMessage(Message msg) {
    696             boolean retVal;
    697 
    698             switch (msg.what) {
    699                 case EVENT_DISCONNECT:
    700                     if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT");
    701                     DisconnectParams dp = (DisconnectParams) msg.obj;
    702                     dp.tag = mTag;
    703                     tearDownData(dp);
    704                     transitionTo(mDisconnectingState);
    705                     retVal = true;
    706                     break;
    707 
    708                 default:
    709                     if (DBG) log("DcActiveState nothandled msg.what=" + msg.what);
    710                     retVal = false;
    711                     break;
    712             }
    713             return retVal;
    714         }
    715     }
    716     private DcActiveState mActiveState = new DcActiveState();
    717 
    718     /**
    719      * The state machine is disconnecting.
    720      */
    721     private class DcDisconnectingState extends HierarchicalState {
    722         @Override protected boolean processMessage(Message msg) {
    723             boolean retVal;
    724 
    725             switch (msg.what) {
    726                 case EVENT_DEACTIVATE_DONE:
    727                     if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE");
    728                     AsyncResult ar = (AsyncResult) msg.obj;
    729                     DisconnectParams dp = (DisconnectParams) ar.userObj;
    730                     if (dp.tag == mTag) {
    731                         // Transition to inactive but send notifications after
    732                         // we've entered the mInactive state.
    733                         mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
    734                         transitionTo(mInactiveState);
    735                     } else {
    736                         if (DBG) log("DcDisconnectState EVENT_DEACTIVATE_DONE stale dp.tag="
    737                                 + dp.tag + " mTag=" + mTag);
    738                     }
    739                     retVal = true;
    740                     break;
    741 
    742                 default:
    743                     if (DBG) log("DcDisconnectingState not handled msg.what=" + msg.what);
    744                     retVal = false;
    745                     break;
    746             }
    747             return retVal;
    748         }
    749     }
    750     private DcDisconnectingState mDisconnectingState = new DcDisconnectingState();
    751 
    752     /**
    753      * The state machine is disconnecting after a bad dns setup
    754      * was found in mInactivatingState.
    755      */
    756     private class DcDisconnectingBadDnsState extends HierarchicalState {
    757         @Override protected boolean processMessage(Message msg) {
    758             boolean retVal;
    759 
    760             switch (msg.what) {
    761                 case EVENT_DEACTIVATE_DONE:
    762                     AsyncResult ar = (AsyncResult) msg.obj;
    763                     ConnectionParams cp = (ConnectionParams) ar.userObj;
    764                     if (cp.tag == mTag) {
    765                         if (DBG) log("DcDisconnectingBadDnsState msg.what=EVENT_DEACTIVATE_DONE");
    766                         // Transition to inactive but send notifications after
    767                         // we've entered the mInactive state.
    768                         mInactiveState.setEnterNotificationParams(cp, FailCause.UNKNOWN);
    769                         transitionTo(mInactiveState);
    770                     } else {
    771                         if (DBG) log("DcDisconnectingBadDnsState EVENT_DEACTIVE_DONE stale dp.tag="
    772                                 + cp.tag + ", mTag=" + mTag);
    773                     }
    774                     retVal = true;
    775                     break;
    776 
    777                 default:
    778                     if (DBG) log("DcDisconnectingBadDnsState not handled msg.what=" + msg.what);
    779                     retVal = false;
    780                     break;
    781             }
    782             return retVal;
    783         }
    784     }
    785     private DcDisconnectingBadDnsState mDisconnectingBadDnsState = new DcDisconnectingBadDnsState();
    786 
    787     // ******* public interface
    788 
    789     /**
    790      * Disconnect from the network.
    791      *
    792      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
    793      *        With AsyncResult.userObj set to the original msg.obj.
    794      */
    795     public void reset(Message onCompletedMsg) {
    796         sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(onCompletedMsg)));
    797     }
    798 
    799     /**
    800      * Reset the connection and wait for it to complete.
    801      * TODO: Remove when all callers only need the asynchronous
    802      * reset defined above.
    803      */
    804     public void resetSynchronously() {
    805         ResetSynchronouslyLock lockObj = new ResetSynchronouslyLock();
    806         synchronized(lockObj) {
    807             sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(lockObj)));
    808             try {
    809                 lockObj.wait();
    810             } catch (InterruptedException e) {
    811                 log("blockingReset: unexpected interrupted of wait()");
    812             }
    813         }
    814     }
    815 
    816     /**
    817      * Connect to the apn and return an AsyncResult in onCompletedMsg.
    818      * Used for cellular networks that use Acess Point Names (APN) such
    819      * as GSM networks.
    820      *
    821      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
    822      *        With AsyncResult.userObj set to the original msg.obj,
    823      *        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
    824      * @param apn is the Acces Point Name to connect to
    825      */
    826     public void connect(Message onCompletedMsg, ApnSetting apn) {
    827         sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
    828     }
    829 
    830     /**
    831      * Connect to the apn and return an AsyncResult in onCompletedMsg.
    832      *
    833      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
    834      *        With AsyncResult.userObj set to the original msg.obj,
    835      *        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
    836      */
    837     public void connect(Message onCompletedMsg) {
    838         sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(null, onCompletedMsg)));
    839     }
    840 
    841     /**
    842      * Disconnect from the network.
    843      *
    844      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
    845      *        With AsyncResult.userObj set to the original msg.obj.
    846      */
    847     public void disconnect(Message onCompletedMsg) {
    848         sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(onCompletedMsg)));
    849     }
    850 
    851     // ****** The following are used for debugging.
    852 
    853     /**
    854      * TODO: This should be an asynchronous call and we wouldn't
    855      * have to use handle the notification in the DcInactiveState.enter.
    856      *
    857      * @return true if the state machine is in the inactive state.
    858      */
    859     public boolean isInactive() {
    860         boolean retVal = getCurrentState() == mInactiveState;
    861         return retVal;
    862     }
    863 
    864     /**
    865      * TODO: This should be an asynchronous call and we wouldn't
    866      * have to use handle the notification in the DcActiveState.enter.
    867      *
    868      * @return true if the state machine is in the active state.
    869      */
    870     public boolean isActive() {
    871         boolean retVal = getCurrentState() == mActiveState;
    872         return retVal;
    873     }
    874 
    875     /**
    876      * @return the interface name as a string.
    877      */
    878     public String getInterface() {
    879         return interfaceName;
    880     }
    881 
    882     /**
    883      * @return the ip address as a string.
    884      */
    885     public String getIpAddress() {
    886         return ipAddress;
    887     }
    888 
    889     /**
    890      * @return the gateway address as a string.
    891      */
    892     public String getGatewayAddress() {
    893         return gatewayAddress;
    894     }
    895 
    896     /**
    897      * @return an array of associated DNS addresses.
    898      */
    899     public String[] getDnsServers() {
    900         return dnsServers;
    901     }
    902 
    903     /**
    904      * @return the current state as a string.
    905      */
    906     public String getStateAsString() {
    907         String retVal = getCurrentState().getName();
    908         return retVal;
    909     }
    910 
    911     /**
    912      * @return the time of when this connection was created.
    913      */
    914     public long getConnectionTime() {
    915         return createTime;
    916     }
    917 
    918     /**
    919      * @return the time of the last failure.
    920      */
    921     public long getLastFailTime() {
    922         return lastFailTime;
    923     }
    924 
    925     /**
    926      * @return the last cause of failure.
    927      */
    928     public FailCause getLastFailCause() {
    929         return lastFailCause;
    930     }
    931 }
    932