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