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
17package com.android.internal.telephony.dataconnection;
18
19
20import com.android.internal.telephony.CommandException;
21import com.android.internal.telephony.DctConstants;
22import com.android.internal.telephony.Phone;
23import com.android.internal.telephony.PhoneBase;
24import com.android.internal.telephony.PhoneConstants;
25import com.android.internal.telephony.RILConstants;
26import com.android.internal.telephony.RetryManager;
27import com.android.internal.util.AsyncChannel;
28import com.android.internal.util.Protocol;
29import com.android.internal.util.State;
30import com.android.internal.util.StateMachine;
31
32import android.app.PendingIntent;
33import android.net.LinkCapabilities;
34import android.net.LinkProperties;
35import android.net.ProxyProperties;
36import android.os.AsyncResult;
37import android.os.Build;
38import android.os.Message;
39import android.os.SystemClock;
40import android.os.SystemProperties;
41import android.telephony.Rlog;
42import android.telephony.ServiceState;
43import android.telephony.TelephonyManager;
44import android.text.TextUtils;
45import android.util.Pair;
46import android.util.Patterns;
47import android.util.TimeUtils;
48
49import java.io.FileDescriptor;
50import java.io.PrintWriter;
51import java.util.ArrayList;
52import java.util.List;
53import 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 */
73public 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                            if (cause == DcFailCause.NONE) {
1333                                if (DBG) {
1334                                    log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"
1335                                            + " BAD: error was NONE, change to UNKNOWN");
1336                                }
1337                                cause = DcFailCause.UNKNOWN;
1338                            }
1339                        }
1340                        mDcFailCause = cause;
1341
1342                        int retryDelay = mRetryManager.getRetryTimer();
1343                        if (DBG) {
1344                            log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"
1345                                    + " cause=" + cause
1346                                    + " retryDelay=" + retryDelay
1347                                    + " isRetryNeeded=" + mRetryManager.isRetryNeeded()
1348                                    + " dc=" + DataConnection.this);
1349                        }
1350                        if (cause.isRestartRadioFail()) {
1351                            if (DBG) {
1352                                log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE"
1353                                        + " restart radio");
1354                            }
1355                            mDct.sendRestartRadio();
1356                            mInactiveState.setEnterNotificationParams(cp, cause);
1357                            transitionTo(mInactiveState);
1358                        } else if (cause.isPermanentFail()) {
1359                            if (DBG) log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE perm er");
1360                            mInactiveState.setEnterNotificationParams(cp, cause);
1361                            transitionTo(mInactiveState);
1362                        } else if ((retryDelay >= 0) && (mRetryManager.isRetryNeeded())) {
1363                            if (DBG) log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE retry");
1364                            mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag,
1365                                                            retryDelay);
1366                            transitionTo(mRetryingState);
1367                        } else {
1368                            if (DBG) log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE no retry");
1369                            mInactiveState.setEnterNotificationParams(cp, cause);
1370                            transitionTo(mInactiveState);
1371                        }
1372                    } else {
1373                        loge("DcActivatingState: stale EVENT_GET_LAST_FAIL_DONE"
1374                                + " tag:" + cp.mTag + " != mTag:" + mTag);
1375                    }
1376
1377                    retVal = HANDLED;
1378                    break;
1379
1380                default:
1381                    if (VDBG) {
1382                        log("DcActivatingState not handled msg.what=" +
1383                                getWhatToString(msg.what) + " RefCount=" + mApnContexts.size());
1384                    }
1385                    retVal = NOT_HANDLED;
1386                    break;
1387            }
1388            return retVal;
1389        }
1390    }
1391    private DcActivatingState mActivatingState = new DcActivatingState();
1392
1393    /**
1394     * The state machine is connected, expecting an EVENT_DISCONNECT.
1395     */
1396    private class DcActiveState extends State {
1397        @Override public void enter() {
1398            if (DBG) log("DcActiveState: enter dc=" + DataConnection.this);
1399
1400            if (mRetryManager.getRetryCount() != 0) {
1401                log("DcActiveState: connected after retrying call notifyAllOfConnected");
1402                mRetryManager.setRetryCount(0);
1403            }
1404            // If we were retrying there maybe more than one, otherwise they'll only be one.
1405            notifyAllOfConnected(Phone.REASON_CONNECTED);
1406
1407            // If the EVENT_CONNECT set the current max retry restore it here
1408            // if it didn't then this is effectively a NOP.
1409            mRetryManager.restoreCurMaxRetryCount();
1410            mDcController.addActiveDcByCid(DataConnection.this);
1411        }
1412
1413        @Override
1414        public void exit() {
1415            if (DBG) log("DcActiveState: exit dc=" + this);
1416        }
1417
1418        @Override
1419        public boolean processMessage(Message msg) {
1420            boolean retVal;
1421
1422            switch (msg.what) {
1423                case EVENT_CONNECT: {
1424                    ConnectionParams cp = (ConnectionParams) msg.obj;
1425                    if (DBG) {
1426                        log("DcActiveState: EVENT_CONNECT cp=" + cp + " dc=" + DataConnection.this);
1427                    }
1428                    if (mApnContexts.contains(cp.mApnContext)) {
1429                        log("DcActiveState ERROR already added apnContext=" + cp.mApnContext);
1430                    } else {
1431                        mApnContexts.add(cp.mApnContext);
1432                        if (DBG) {
1433                            log("DcActiveState msg.what=EVENT_CONNECT RefCount="
1434                                    + mApnContexts.size());
1435                        }
1436                    }
1437                    notifyConnectCompleted(cp, DcFailCause.NONE, false);
1438                    retVal = HANDLED;
1439                    break;
1440                }
1441                case EVENT_DISCONNECT: {
1442                    DisconnectParams dp = (DisconnectParams) msg.obj;
1443                    if (DBG) {
1444                        log("DcActiveState: EVENT_DISCONNECT dp=" + dp
1445                                + " dc=" + DataConnection.this);
1446                    }
1447                    if (mApnContexts.contains(dp.mApnContext)) {
1448                        if (DBG) {
1449                            log("DcActiveState msg.what=EVENT_DISCONNECT RefCount="
1450                                    + mApnContexts.size());
1451                        }
1452
1453                        if (mApnContexts.size() == 1) {
1454                            mApnContexts.clear();
1455                            mDisconnectParams = dp;
1456                            mConnectionParams = null;
1457                            dp.mTag = mTag;
1458                            tearDownData(dp);
1459                            transitionTo(mDisconnectingState);
1460                        } else {
1461                            mApnContexts.remove(dp.mApnContext);
1462                            notifyDisconnectCompleted(dp, false);
1463                        }
1464                    } else {
1465                        log("DcActiveState ERROR no such apnContext=" + dp.mApnContext
1466                                + " in this dc=" + DataConnection.this);
1467                        notifyDisconnectCompleted(dp, false);
1468                    }
1469                    retVal = HANDLED;
1470                    break;
1471                }
1472                case EVENT_DISCONNECT_ALL: {
1473                    if (DBG) {
1474                        log("DcActiveState EVENT_DISCONNECT clearing apn contexts,"
1475                                + " dc=" + DataConnection.this);
1476                    }
1477                    mApnContexts.clear();
1478                    DisconnectParams dp = (DisconnectParams) msg.obj;
1479                    mDisconnectParams = dp;
1480                    mConnectionParams = null;
1481                    dp.mTag = mTag;
1482                    tearDownData(dp);
1483                    transitionTo(mDisconnectingState);
1484                    retVal = HANDLED;
1485                    break;
1486                }
1487                case EVENT_LOST_CONNECTION: {
1488                    if (DBG) {
1489                        log("DcActiveState EVENT_LOST_CONNECTION dc=" + DataConnection.this);
1490                    }
1491                    if (mRetryManager.isRetryNeeded()) {
1492                        // We're going to retry
1493                        int delayMillis = mRetryManager.getRetryTimer();
1494                        if (DBG) {
1495                            log("DcActiveState EVENT_LOST_CONNECTION startRetryAlarm"
1496                                    + " mTag=" + mTag + " delay=" + delayMillis + "ms");
1497                        }
1498                        mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag,
1499                                delayMillis);
1500                        transitionTo(mRetryingState);
1501                    } else {
1502                        mInactiveState.setEnterNotificationParams(DcFailCause.LOST_CONNECTION);
1503                        transitionTo(mInactiveState);
1504                    }
1505                    retVal = HANDLED;
1506                    break;
1507                }
1508                default:
1509                    if (VDBG) {
1510                        log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
1511                    }
1512                    retVal = NOT_HANDLED;
1513                    break;
1514            }
1515            return retVal;
1516        }
1517    }
1518    private DcActiveState mActiveState = new DcActiveState();
1519
1520    /**
1521     * The state machine is disconnecting.
1522     */
1523    private class DcDisconnectingState extends State {
1524        @Override
1525        public boolean processMessage(Message msg) {
1526            boolean retVal;
1527
1528            switch (msg.what) {
1529                case EVENT_CONNECT:
1530                    if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
1531                            + mApnContexts.size());
1532                    deferMessage(msg);
1533                    retVal = HANDLED;
1534                    break;
1535
1536                case EVENT_DEACTIVATE_DONE:
1537                    if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
1538                            + mApnContexts.size());
1539                    AsyncResult ar = (AsyncResult) msg.obj;
1540                    DisconnectParams dp = (DisconnectParams) ar.userObj;
1541                    if (dp.mTag == mTag) {
1542                        // Transition to inactive but send notifications after
1543                        // we've entered the mInactive state.
1544                        mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
1545                        transitionTo(mInactiveState);
1546                    } else {
1547                        if (DBG) log("DcDisconnectState stale EVENT_DEACTIVATE_DONE"
1548                                + " dp.tag=" + dp.mTag + " mTag=" + mTag);
1549                    }
1550                    retVal = HANDLED;
1551                    break;
1552
1553                default:
1554                    if (VDBG) {
1555                        log("DcDisconnectingState not handled msg.what="
1556                                + getWhatToString(msg.what));
1557                    }
1558                    retVal = NOT_HANDLED;
1559                    break;
1560            }
1561            return retVal;
1562        }
1563    }
1564    private DcDisconnectingState mDisconnectingState = new DcDisconnectingState();
1565
1566    /**
1567     * The state machine is disconnecting after an creating a connection.
1568     */
1569    private class DcDisconnectionErrorCreatingConnection extends State {
1570        @Override
1571        public boolean processMessage(Message msg) {
1572            boolean retVal;
1573
1574            switch (msg.what) {
1575                case EVENT_DEACTIVATE_DONE:
1576                    AsyncResult ar = (AsyncResult) msg.obj;
1577                    ConnectionParams cp = (ConnectionParams) ar.userObj;
1578                    if (cp.mTag == mTag) {
1579                        if (DBG) {
1580                            log("DcDisconnectionErrorCreatingConnection" +
1581                                " msg.what=EVENT_DEACTIVATE_DONE");
1582                        }
1583
1584                        // Transition to inactive but send notifications after
1585                        // we've entered the mInactive state.
1586                        mInactiveState.setEnterNotificationParams(cp,
1587                                DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER);
1588                        transitionTo(mInactiveState);
1589                    } else {
1590                        if (DBG) {
1591                            log("DcDisconnectionErrorCreatingConnection stale EVENT_DEACTIVATE_DONE"
1592                                    + " dp.tag=" + cp.mTag + ", mTag=" + mTag);
1593                        }
1594                    }
1595                    retVal = HANDLED;
1596                    break;
1597
1598                default:
1599                    if (VDBG) {
1600                        log("DcDisconnectionErrorCreatingConnection not handled msg.what="
1601                                + getWhatToString(msg.what));
1602                    }
1603                    retVal = NOT_HANDLED;
1604                    break;
1605            }
1606            return retVal;
1607        }
1608    }
1609    private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection =
1610                new DcDisconnectionErrorCreatingConnection();
1611
1612    // ******* "public" interface
1613
1614    /**
1615     * Used for testing purposes.
1616     */
1617    /* package */ void tearDownNow() {
1618        if (DBG) log("tearDownNow()");
1619        sendMessage(obtainMessage(EVENT_TEAR_DOWN_NOW));
1620    }
1621
1622    /**
1623     * @return the string for msg.what as our info.
1624     */
1625    @Override
1626    protected String getWhatToString(int what) {
1627        return cmdToString(what);
1628    }
1629
1630    private static String msgToString(Message msg) {
1631        String retVal;
1632        if (msg == null) {
1633            retVal = "null";
1634        } else {
1635            StringBuilder   b = new StringBuilder();
1636
1637            b.append("{what=");
1638            b.append(cmdToString(msg.what));
1639
1640            b.append(" when=");
1641            TimeUtils.formatDuration(msg.getWhen() - SystemClock.uptimeMillis(), b);
1642
1643            if (msg.arg1 != 0) {
1644                b.append(" arg1=");
1645                b.append(msg.arg1);
1646            }
1647
1648            if (msg.arg2 != 0) {
1649                b.append(" arg2=");
1650                b.append(msg.arg2);
1651            }
1652
1653            if (msg.obj != null) {
1654                b.append(" obj=");
1655                b.append(msg.obj);
1656            }
1657
1658            b.append(" target=");
1659            b.append(msg.getTarget());
1660
1661            b.append(" replyTo=");
1662            b.append(msg.replyTo);
1663
1664            b.append("}");
1665
1666            retVal = b.toString();
1667        }
1668        return retVal;
1669    }
1670
1671    static void slog(String s) {
1672        Rlog.d("DC", s);
1673    }
1674
1675    /**
1676     * Log with debug
1677     *
1678     * @param s is string log
1679     */
1680    @Override
1681    protected void log(String s) {
1682        Rlog.d(getName(), s);
1683    }
1684
1685    /**
1686     * Log with debug attribute
1687     *
1688     * @param s is string log
1689     */
1690    @Override
1691    protected void logd(String s) {
1692        Rlog.d(getName(), s);
1693    }
1694
1695    /**
1696     * Log with verbose attribute
1697     *
1698     * @param s is string log
1699     */
1700    @Override
1701    protected void logv(String s) {
1702        Rlog.v(getName(), s);
1703    }
1704
1705    /**
1706     * Log with info attribute
1707     *
1708     * @param s is string log
1709     */
1710    @Override
1711    protected void logi(String s) {
1712        Rlog.i(getName(), s);
1713    }
1714
1715    /**
1716     * Log with warning attribute
1717     *
1718     * @param s is string log
1719     */
1720    @Override
1721    protected void logw(String s) {
1722        Rlog.w(getName(), s);
1723    }
1724
1725    /**
1726     * Log with error attribute
1727     *
1728     * @param s is string log
1729     */
1730    @Override
1731    protected void loge(String s) {
1732        Rlog.e(getName(), s);
1733    }
1734
1735    /**
1736     * Log with error attribute
1737     *
1738     * @param s is string log
1739     * @param e is a Throwable which logs additional information.
1740     */
1741    @Override
1742    protected void loge(String s, Throwable e) {
1743        Rlog.e(getName(), s, e);
1744    }
1745
1746    /** Doesn't print mApnList of ApnContext's which would be recursive */
1747    public String toStringSimple() {
1748        return getName() + ": State=" + getCurrentState().getName()
1749                + " mApnSetting=" + mApnSetting + " RefCount=" + mApnContexts.size()
1750                + " mCid=" + mCid + " mCreateTime=" + mCreateTime
1751                + " mLastastFailTime=" + mLastFailTime
1752                + " mLastFailCause=" + mLastFailCause
1753                + " mTag=" + mTag
1754                + " mRetryManager=" + mRetryManager
1755                + " mLinkProperties=" + mLinkProperties
1756                + " mLinkCapabilities=" + mLinkCapabilities;
1757    }
1758
1759    @Override
1760    public String toString() {
1761        return "{" + toStringSimple() + " mApnContexts=" + mApnContexts + "}";
1762    }
1763
1764    /**
1765     * Dump the current state.
1766     *
1767     * @param fd
1768     * @param pw
1769     * @param args
1770     */
1771    @Override
1772    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1773        pw.print("DataConnection ");
1774        super.dump(fd, pw, args);
1775        pw.println(" mApnContexts.size=" + mApnContexts.size());
1776        pw.println(" mApnContexts=" + mApnContexts);
1777        pw.flush();
1778        pw.println(" mDataConnectionTracker=" + mDct);
1779        pw.println(" mApnSetting=" + mApnSetting);
1780        pw.println(" mTag=" + mTag);
1781        pw.println(" mCid=" + mCid);
1782        pw.println(" mRetryManager=" + mRetryManager);
1783        pw.println(" mConnectionParams=" + mConnectionParams);
1784        pw.println(" mDisconnectParams=" + mDisconnectParams);
1785        pw.println(" mDcFailCause=" + mDcFailCause);
1786        pw.flush();
1787        pw.println(" mPhone=" + mPhone);
1788        pw.flush();
1789        pw.println(" mLinkProperties=" + mLinkProperties);
1790        pw.flush();
1791        pw.println(" mDataRegState=" + mDataRegState);
1792        pw.println(" mRilRat=" + mRilRat);
1793        pw.println(" mLinkCapabilities=" + mLinkCapabilities);
1794        pw.println(" mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
1795        pw.println(" mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime));
1796        pw.println(" mLastFailCause=" + mLastFailCause);
1797        pw.flush();
1798        pw.println(" mUserData=" + mUserData);
1799        pw.println(" mInstanceNumber=" + mInstanceNumber);
1800        pw.println(" mAc=" + mAc);
1801        pw.println(" mDcRetryAlarmController=" + mDcRetryAlarmController);
1802        pw.flush();
1803    }
1804}
1805