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