OSDN Git Service

DataConnection: Clear cid when entering inactive state.
[android-x86/frameworks-base.git] / telephony / java / com / android / internal / telephony / DataConnection.java
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 }