• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
toString()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 {
ConnectionParams(ApnSetting apn, Message onCompletedMsg)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 {
DisconnectParams(Message onCompletedMsg)148         public DisconnectParams(Message onCompletedMsg) {
149             this.onCompletedMsg = onCompletedMsg;
150         }
DisconnectParams(ResetSynchronouslyLock lockObj)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 
isPermanentFail()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 
isEventLoggable()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
toString()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
toString()269     public abstract String toString();
270 
onConnect(ConnectionParams cp)271     protected abstract void onConnect(ConnectionParams cp);
272 
getFailCauseFromRequest(int rilCause)273     protected abstract FailCause getFailCauseFromRequest(int rilCause);
274 
isDnsOk(String[] domainNameServers)275     protected abstract boolean isDnsOk(String[] domainNameServers);
276 
log(String s)277     protected abstract void log(String s);
278 
279 
280    //***** Constructor
DataConnection(PhoneBase phone, String name)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      */
tearDownData(Object o)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      */
notifyConnectCompleted(ConnectionParams cp, FailCause cause)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      */
notifyDisconnectCompleted(DisconnectParams dp)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      */
clearSettings()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      */
onSetupConnectionCompleted(AsyncResult ar)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
processMessage(Message msg)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 
setEnterNotificationParams(ConnectionParams cp, FailCause cause)500         public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
501             log("DcInactiveState: setEnterNoticationParams cp,cause");
502             mConnectionParams = cp;
503             mFailCause = cause;
504         }
505 
setEnterNotificationParams(DisconnectParams dp)506         public void setEnterNotificationParams(DisconnectParams dp) {
507           log("DcInactiveState: setEnterNoticationParams dp");
508             mDisconnectParams = dp;
509         }
510 
enter()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 
exit()531         @Override protected void exit() {
532             // clear notifications
533             mConnectionParams = null;
534             mFailCause = null;
535             mDisconnectParams = null;
536         }
537 
processMessage(Message msg)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 {
processMessage(Message msg)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 
setEnterNotificationParams(ConnectionParams cp, FailCause cause)669         public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
670             log("DcInactiveState: setEnterNoticationParams cp,cause");
671             mConnectionParams = cp;
672             mFailCause = cause;
673         }
674 
enter()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 
exit()689         @Override protected void exit() {
690             // clear notifications
691             mConnectionParams = null;
692             mFailCause = null;
693         }
694 
processMessage(Message msg)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 {
processMessage(Message msg)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 {
processMessage(Message msg)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      */
reset(Message onCompletedMsg)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      */
resetSynchronously()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      */
connect(Message onCompletedMsg, ApnSetting apn)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      */
connect(Message onCompletedMsg)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      */
disconnect(Message onCompletedMsg)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      */
isInactive()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      */
isActive()870     public boolean isActive() {
871         boolean retVal = getCurrentState() == mActiveState;
872         return retVal;
873     }
874 
875     /**
876      * @return the interface name as a string.
877      */
getInterface()878     public String getInterface() {
879         return interfaceName;
880     }
881 
882     /**
883      * @return the ip address as a string.
884      */
getIpAddress()885     public String getIpAddress() {
886         return ipAddress;
887     }
888 
889     /**
890      * @return the gateway address as a string.
891      */
getGatewayAddress()892     public String getGatewayAddress() {
893         return gatewayAddress;
894     }
895 
896     /**
897      * @return an array of associated DNS addresses.
898      */
getDnsServers()899     public String[] getDnsServers() {
900         return dnsServers;
901     }
902 
903     /**
904      * @return the current state as a string.
905      */
getStateAsString()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      */
getConnectionTime()914     public long getConnectionTime() {
915         return createTime;
916     }
917 
918     /**
919      * @return the time of the last failure.
920      */
getLastFailTime()921     public long getLastFailTime() {
922         return lastFailTime;
923     }
924 
925     /**
926      * @return the last cause of failure.
927      */
getLastFailCause()928     public FailCause getLastFailCause() {
929         return lastFailCause;
930     }
931 }
932