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                    transitionTo(mInactiveState);
1201                    retVal = HANDLED;
1202                    break;
1203                }
1204                default: {
1205                    if (VDBG) {
1206                        log("DcRetryingState nothandled msg.what=" + getWhatToString(msg.what));
1207                    }
1208                    retVal = NOT_HANDLED;
1209                    break;
1210                }
1211            }
1212            return retVal;
1213        }
1214    }
1215    private DcRetryingState mRetryingState = new DcRetryingState();
1216
1217    /**
1218     * The state machine is activating a connection.
1219     */
1220    private class DcActivatingState extends State {
1221        @Override
1222        public boolean processMessage(Message msg) {
1223            boolean retVal;
1224            AsyncResult ar;
1225            ConnectionParams cp;
1226
1227            if (DBG) log("DcActivatingState: msg=" + msgToString(msg));
1228            switch (msg.what) {
1229                case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED:
1230                case EVENT_CONNECT:
1231                    // Activating can't process until we're done.
1232                    deferMessage(msg);
1233                    retVal = HANDLED;
1234                    break;
1235
1236                case EVENT_SETUP_DATA_CONNECTION_DONE:
1237                    ar = (AsyncResult) msg.obj;
1238                    cp = (ConnectionParams) ar.userObj;
1239
1240                    DataCallResponse.SetupResult result = onSetupConnectionCompleted(ar);
1241                    if (result != DataCallResponse.SetupResult.ERR_Stale) {
1242                        if (mConnectionParams != cp) {
1243                            loge("DcActivatingState: WEIRD mConnectionsParams:"+ mConnectionParams
1244                                    + " != cp:" + cp);
1245                        }
1246                    }
1247                    if (DBG) {
1248                        log("DcActivatingState onSetupConnectionCompleted result=" + result
1249                                + " dc=" + DataConnection.this);
1250                    }
1251                    switch (result) {
1252                        case SUCCESS:
1253                            // All is well
1254                            mDcFailCause = DcFailCause.NONE;
1255                            transitionTo(mActiveState);
1256                            break;
1257                        case ERR_BadCommand:
1258                            // Vendor ril rejected the command and didn't connect.
1259                            // Transition to inactive but send notifications after
1260                            // we've entered the mInactive state.
1261                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
1262                            transitionTo(mInactiveState);
1263                            break;
1264                        case ERR_UnacceptableParameter:
1265                            // The addresses given from the RIL are bad
1266                            tearDownData(cp);
1267                            transitionTo(mDisconnectingErrorCreatingConnection);
1268                            break;
1269                        case ERR_GetLastErrorFromRil:
1270                            // Request failed and this is an old RIL
1271                            mPhone.mCi.getLastDataCallFailCause(
1272                                    obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp));
1273                            break;
1274                        case ERR_RilError:
1275                            int delay = mDcRetryAlarmController.getSuggestedRetryTime(
1276                                                                    DataConnection.this, ar);
1277                            if (DBG) {
1278                                log("DcActivatingState: ERR_RilError "
1279                                        + " delay=" + delay
1280                                        + " isRetryNeeded=" + mRetryManager.isRetryNeeded()
1281                                        + " result=" + result
1282                                        + " result.isRestartRadioFail=" +
1283                                                result.mFailCause.isRestartRadioFail()
1284                                        + " result.isPermanentFail=" +
1285                                                result.mFailCause.isPermanentFail());
1286                            }
1287                            if (result.mFailCause.isRestartRadioFail()) {
1288                                if (DBG) log("DcActivatingState: ERR_RilError restart radio");
1289                                mDct.sendRestartRadio();
1290                                mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
1291                                transitionTo(mInactiveState);
1292                            } else if (result.mFailCause.isPermanentFail()) {
1293                                if (DBG) log("DcActivatingState: ERR_RilError perm error");
1294                                mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
1295                                transitionTo(mInactiveState);
1296                            } else if (delay >= 0) {
1297                                if (DBG) log("DcActivatingState: ERR_RilError retry");
1298                                mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION,
1299                                                            mTag, delay);
1300                                transitionTo(mRetryingState);
1301                            } else {
1302                                if (DBG) log("DcActivatingState: ERR_RilError no retry");
1303                                mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
1304                                transitionTo(mInactiveState);
1305                            }
1306                            break;
1307                        case ERR_Stale:
1308                            loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE"
1309                                    + " tag:" + cp.mTag + " != mTag:" + mTag);
1310                            break;
1311                        default:
1312                            throw new RuntimeException("Unknown SetupResult, should not happen");
1313                    }
1314                    retVal = HANDLED;
1315                    break;
1316
1317                case EVENT_GET_LAST_FAIL_DONE:
1318                    ar = (AsyncResult) msg.obj;
1319                    cp = (ConnectionParams) ar.userObj;
1320                    if (cp.mTag == mTag) {
1321                        if (mConnectionParams != cp) {
1322                            loge("DcActivatingState: WEIRD mConnectionsParams:" + mConnectionParams
1323                                    + " != cp:" + cp);
1324                        }
1325
1326                        DcFailCause cause = DcFailCause.UNKNOWN;
1327
1328                        if (ar.exception == null) {
1329                            int rilFailCause = ((int[]) (ar.result))[0];
1330                            cause = DcFailCause.fromInt(rilFailCause);
1331                            if (cause == DcFailCause.NONE) {
1332                                if (DBG) {
1333                                    log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"
1334                                            + " BAD: error was NONE, change to UNKNOWN");
1335                                }
1336                                cause = DcFailCause.UNKNOWN;
1337                            }
1338                        }
1339                        mDcFailCause = cause;
1340
1341                        int retryDelay = mRetryManager.getRetryTimer();
1342                        if (DBG) {
1343                            log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"
1344                                    + " cause=" + cause
1345                                    + " retryDelay=" + retryDelay
1346                                    + " isRetryNeeded=" + mRetryManager.isRetryNeeded()
1347                                    + " dc=" + DataConnection.this);
1348                        }
1349                        if (cause.isRestartRadioFail()) {
1350                            if (DBG) {
1351                                log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE"
1352                                        + " restart radio");
1353                            }
1354                            mDct.sendRestartRadio();
1355                            mInactiveState.setEnterNotificationParams(cp, cause);
1356                            transitionTo(mInactiveState);
1357                        } else if (cause.isPermanentFail()) {
1358                            if (DBG) log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE perm er");
1359                            mInactiveState.setEnterNotificationParams(cp, cause);
1360                            transitionTo(mInactiveState);
1361                        } else if ((retryDelay >= 0) && (mRetryManager.isRetryNeeded())) {
1362                            if (DBG) log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE retry");
1363                            mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag,
1364                                                            retryDelay);
1365                            transitionTo(mRetryingState);
1366                        } else {
1367                            if (DBG) log("DcActivatingState: EVENT_GET_LAST_FAIL_DONE no retry");
1368                            mInactiveState.setEnterNotificationParams(cp, cause);
1369                            transitionTo(mInactiveState);
1370                        }
1371                    } else {
1372                        loge("DcActivatingState: stale EVENT_GET_LAST_FAIL_DONE"
1373                                + " tag:" + cp.mTag + " != mTag:" + mTag);
1374                    }
1375
1376                    retVal = HANDLED;
1377                    break;
1378
1379                default:
1380                    if (VDBG) {
1381                        log("DcActivatingState not handled msg.what=" +
1382                                getWhatToString(msg.what) + " RefCount=" + mApnContexts.size());
1383                    }
1384                    retVal = NOT_HANDLED;
1385                    break;
1386            }
1387            return retVal;
1388        }
1389    }
1390    private DcActivatingState mActivatingState = new DcActivatingState();
1391
1392    /**
1393     * The state machine is connected, expecting an EVENT_DISCONNECT.
1394     */
1395    private class DcActiveState extends State {
1396        @Override public void enter() {
1397            if (DBG) log("DcActiveState: enter dc=" + DataConnection.this);
1398
1399            if (mRetryManager.getRetryCount() != 0) {
1400                log("DcActiveState: connected after retrying call notifyAllOfConnected");
1401                mRetryManager.setRetryCount(0);
1402            }
1403            // If we were retrying there maybe more than one, otherwise they'll only be one.
1404            notifyAllOfConnected(Phone.REASON_CONNECTED);
1405
1406            // If the EVENT_CONNECT set the current max retry restore it here
1407            // if it didn't then this is effectively a NOP.
1408            mRetryManager.restoreCurMaxRetryCount();
1409            mDcController.addActiveDcByCid(DataConnection.this);
1410        }
1411
1412        @Override
1413        public void exit() {
1414            if (DBG) log("DcActiveState: exit dc=" + this);
1415        }
1416
1417        @Override
1418        public boolean processMessage(Message msg) {
1419            boolean retVal;
1420
1421            switch (msg.what) {
1422                case EVENT_CONNECT: {
1423                    ConnectionParams cp = (ConnectionParams) msg.obj;
1424                    if (DBG) {
1425                        log("DcActiveState: EVENT_CONNECT cp=" + cp + " dc=" + DataConnection.this);
1426                    }
1427                    if (mApnContexts.contains(cp.mApnContext)) {
1428                        log("DcActiveState ERROR already added apnContext=" + cp.mApnContext);
1429                    } else {
1430                        mApnContexts.add(cp.mApnContext);
1431                        if (DBG) {
1432                            log("DcActiveState msg.what=EVENT_CONNECT RefCount="
1433                                    + mApnContexts.size());
1434                        }
1435                    }
1436                    notifyConnectCompleted(cp, DcFailCause.NONE, false);
1437                    retVal = HANDLED;
1438                    break;
1439                }
1440                case EVENT_DISCONNECT: {
1441                    DisconnectParams dp = (DisconnectParams) msg.obj;
1442                    if (DBG) {
1443                        log("DcActiveState: EVENT_DISCONNECT dp=" + dp
1444                                + " dc=" + DataConnection.this);
1445                    }
1446                    if (mApnContexts.contains(dp.mApnContext)) {
1447                        if (DBG) {
1448                            log("DcActiveState msg.what=EVENT_DISCONNECT RefCount="
1449                                    + mApnContexts.size());
1450                        }
1451
1452                        if (mApnContexts.size() == 1) {
1453                            mApnContexts.clear();
1454                            mDisconnectParams = dp;
1455                            mConnectionParams = null;
1456                            dp.mTag = mTag;
1457                            tearDownData(dp);
1458                            transitionTo(mDisconnectingState);
1459                        } else {
1460                            mApnContexts.remove(dp.mApnContext);
1461                            notifyDisconnectCompleted(dp, false);
1462                        }
1463                    } else {
1464                        log("DcActiveState ERROR no such apnContext=" + dp.mApnContext
1465                                + " in this dc=" + DataConnection.this);
1466                        notifyDisconnectCompleted(dp, false);
1467                    }
1468                    retVal = HANDLED;
1469                    break;
1470                }
1471                case EVENT_DISCONNECT_ALL: {
1472                    if (DBG) {
1473                        log("DcActiveState EVENT_DISCONNECT clearing apn contexts,"
1474                                + " dc=" + DataConnection.this);
1475                    }
1476                    DisconnectParams dp = (DisconnectParams) msg.obj;
1477                    mDisconnectParams = dp;
1478                    mConnectionParams = null;
1479                    dp.mTag = mTag;
1480                    tearDownData(dp);
1481                    transitionTo(mDisconnectingState);
1482                    retVal = HANDLED;
1483                    break;
1484                }
1485                case EVENT_LOST_CONNECTION: {
1486                    if (DBG) {
1487                        log("DcActiveState EVENT_LOST_CONNECTION dc=" + DataConnection.this);
1488                    }
1489                    if (mRetryManager.isRetryNeeded()) {
1490                        // We're going to retry
1491                        int delayMillis = mRetryManager.getRetryTimer();
1492                        if (DBG) {
1493                            log("DcActiveState EVENT_LOST_CONNECTION startRetryAlarm"
1494                                    + " mTag=" + mTag + " delay=" + delayMillis + "ms");
1495                        }
1496                        mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag,
1497                                delayMillis);
1498                        transitionTo(mRetryingState);
1499                    } else {
1500                        mInactiveState.setEnterNotificationParams(DcFailCause.LOST_CONNECTION);
1501                        transitionTo(mInactiveState);
1502                    }
1503                    retVal = HANDLED;
1504                    break;
1505                }
1506                default:
1507                    if (VDBG) {
1508                        log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
1509                    }
1510                    retVal = NOT_HANDLED;
1511                    break;
1512            }
1513            return retVal;
1514        }
1515    }
1516    private DcActiveState mActiveState = new DcActiveState();
1517
1518    /**
1519     * The state machine is disconnecting.
1520     */
1521    private class DcDisconnectingState extends State {
1522        @Override
1523        public boolean processMessage(Message msg) {
1524            boolean retVal;
1525
1526            switch (msg.what) {
1527                case EVENT_CONNECT:
1528                    if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
1529                            + mApnContexts.size());
1530                    deferMessage(msg);
1531                    retVal = HANDLED;
1532                    break;
1533
1534                case EVENT_DEACTIVATE_DONE:
1535                    if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
1536                            + mApnContexts.size());
1537                    AsyncResult ar = (AsyncResult) msg.obj;
1538                    DisconnectParams dp = (DisconnectParams) ar.userObj;
1539                    if (dp.mTag == mTag) {
1540                        // Transition to inactive but send notifications after
1541                        // we've entered the mInactive state.
1542                        mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
1543                        transitionTo(mInactiveState);
1544                    } else {
1545                        if (DBG) log("DcDisconnectState stale EVENT_DEACTIVATE_DONE"
1546                                + " dp.tag=" + dp.mTag + " mTag=" + mTag);
1547                    }
1548                    retVal = HANDLED;
1549                    break;
1550
1551                default:
1552                    if (VDBG) {
1553                        log("DcDisconnectingState not handled msg.what="
1554                                + getWhatToString(msg.what));
1555                    }
1556                    retVal = NOT_HANDLED;
1557                    break;
1558            }
1559            return retVal;
1560        }
1561    }
1562    private DcDisconnectingState mDisconnectingState = new DcDisconnectingState();
1563
1564    /**
1565     * The state machine is disconnecting after an creating a connection.
1566     */
1567    private class DcDisconnectionErrorCreatingConnection extends State {
1568        @Override
1569        public boolean processMessage(Message msg) {
1570            boolean retVal;
1571
1572            switch (msg.what) {
1573                case EVENT_DEACTIVATE_DONE:
1574                    AsyncResult ar = (AsyncResult) msg.obj;
1575                    ConnectionParams cp = (ConnectionParams) ar.userObj;
1576                    if (cp.mTag == mTag) {
1577                        if (DBG) {
1578                            log("DcDisconnectionErrorCreatingConnection" +
1579                                " msg.what=EVENT_DEACTIVATE_DONE");
1580                        }
1581
1582                        // Transition to inactive but send notifications after
1583                        // we've entered the mInactive state.
1584                        mInactiveState.setEnterNotificationParams(cp,
1585                                DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER);
1586                        transitionTo(mInactiveState);
1587                    } else {
1588                        if (DBG) {
1589                            log("DcDisconnectionErrorCreatingConnection stale EVENT_DEACTIVATE_DONE"
1590                                    + " dp.tag=" + cp.mTag + ", mTag=" + mTag);
1591                        }
1592                    }
1593                    retVal = HANDLED;
1594                    break;
1595
1596                default:
1597                    if (VDBG) {
1598                        log("DcDisconnectionErrorCreatingConnection not handled msg.what="
1599                                + getWhatToString(msg.what));
1600                    }
1601                    retVal = NOT_HANDLED;
1602                    break;
1603            }
1604            return retVal;
1605        }
1606    }
1607    private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection =
1608                new DcDisconnectionErrorCreatingConnection();
1609
1610    // ******* "public" interface
1611
1612    /**
1613     * Used for testing purposes.
1614     */
1615    /* package */ void tearDownNow() {
1616        if (DBG) log("tearDownNow()");
1617        sendMessage(obtainMessage(EVENT_TEAR_DOWN_NOW));
1618    }
1619
1620    /**
1621     * @return the string for msg.what as our info.
1622     */
1623    @Override
1624    protected String getWhatToString(int what) {
1625        return cmdToString(what);
1626    }
1627
1628    private static String msgToString(Message msg) {
1629        String retVal;
1630        if (msg == null) {
1631            retVal = "null";
1632        } else {
1633            StringBuilder   b = new StringBuilder();
1634
1635            b.append("{what=");
1636            b.append(cmdToString(msg.what));
1637
1638            b.append(" when=");
1639            TimeUtils.formatDuration(msg.getWhen() - SystemClock.uptimeMillis(), b);
1640
1641            if (msg.arg1 != 0) {
1642                b.append(" arg1=");
1643                b.append(msg.arg1);
1644            }
1645
1646            if (msg.arg2 != 0) {
1647                b.append(" arg2=");
1648                b.append(msg.arg2);
1649            }
1650
1651            if (msg.obj != null) {
1652                b.append(" obj=");
1653                b.append(msg.obj);
1654            }
1655
1656            b.append(" target=");
1657            b.append(msg.getTarget());
1658
1659            b.append(" replyTo=");
1660            b.append(msg.replyTo);
1661
1662            b.append("}");
1663
1664            retVal = b.toString();
1665        }
1666        return retVal;
1667    }
1668
1669    static void slog(String s) {
1670        Rlog.d("DC", s);
1671    }
1672
1673    /**
1674     * Log with debug
1675     *
1676     * @param s is string log
1677     */
1678    @Override
1679    protected void log(String s) {
1680        Rlog.d(getName(), s);
1681    }
1682
1683    /**
1684     * Log with debug attribute
1685     *
1686     * @param s is string log
1687     */
1688    @Override
1689    protected void logd(String s) {
1690        Rlog.d(getName(), s);
1691    }
1692
1693    /**
1694     * Log with verbose attribute
1695     *
1696     * @param s is string log
1697     */
1698    @Override
1699    protected void logv(String s) {
1700        Rlog.v(getName(), s);
1701    }
1702
1703    /**
1704     * Log with info attribute
1705     *
1706     * @param s is string log
1707     */
1708    @Override
1709    protected void logi(String s) {
1710        Rlog.i(getName(), s);
1711    }
1712
1713    /**
1714     * Log with warning attribute
1715     *
1716     * @param s is string log
1717     */
1718    @Override
1719    protected void logw(String s) {
1720        Rlog.w(getName(), s);
1721    }
1722
1723    /**
1724     * Log with error attribute
1725     *
1726     * @param s is string log
1727     */
1728    @Override
1729    protected void loge(String s) {
1730        Rlog.e(getName(), s);
1731    }
1732
1733    /**
1734     * Log with error attribute
1735     *
1736     * @param s is string log
1737     * @param e is a Throwable which logs additional information.
1738     */
1739    @Override
1740    protected void loge(String s, Throwable e) {
1741        Rlog.e(getName(), s, e);
1742    }
1743
1744    /** Doesn't print mApnList of ApnContext's which would be recursive */
1745    public String toStringSimple() {
1746        return getName() + ": State=" + getCurrentState().getName()
1747                + " mApnSetting=" + mApnSetting + " RefCount=" + mApnContexts.size()
1748                + " mCid=" + mCid + " mCreateTime=" + mCreateTime
1749                + " mLastastFailTime=" + mLastFailTime
1750                + " mLastFailCause=" + mLastFailCause
1751                + " mTag=" + mTag
1752                + " mRetryManager=" + mRetryManager
1753                + " mLinkProperties=" + mLinkProperties
1754                + " mLinkCapabilities=" + mLinkCapabilities;
1755    }
1756
1757    @Override
1758    public String toString() {
1759        return "{" + toStringSimple() + " mApnContexts=" + mApnContexts + "}";
1760    }
1761
1762    /**
1763     * Dump the current state.
1764     *
1765     * @param fd
1766     * @param pw
1767     * @param args
1768     */
1769    @Override
1770    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1771        pw.print("DataConnection ");
1772        super.dump(fd, pw, args);
1773        pw.println(" mApnContexts.size=" + mApnContexts.size());
1774        pw.println(" mApnContexts=" + mApnContexts);
1775        pw.flush();
1776        pw.println(" mDataConnectionTracker=" + mDct);
1777        pw.println(" mApnSetting=" + mApnSetting);
1778        pw.println(" mTag=" + mTag);
1779        pw.println(" mCid=" + mCid);
1780        pw.println(" mRetryManager=" + mRetryManager);
1781        pw.println(" mConnectionParams=" + mConnectionParams);
1782        pw.println(" mDisconnectParams=" + mDisconnectParams);
1783        pw.println(" mDcFailCause=" + mDcFailCause);
1784        pw.flush();
1785        pw.println(" mPhone=" + mPhone);
1786        pw.flush();
1787        pw.println(" mLinkProperties=" + mLinkProperties);
1788        pw.flush();
1789        pw.println(" mDataRegState=" + mDataRegState);
1790        pw.println(" mRilRat=" + mRilRat);
1791        pw.println(" mLinkCapabilities=" + mLinkCapabilities);
1792        pw.println(" mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
1793        pw.println(" mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime));
1794        pw.println(" mLastFailCause=" + mLastFailCause);
1795        pw.flush();
1796        pw.println(" mUserData=" + mUserData);
1797        pw.println(" mInstanceNumber=" + mInstanceNumber);
1798        pw.println(" mAc=" + mAc);
1799        pw.println(" mDcRetryAlarmController=" + mDcRetryAlarmController);
1800        pw.flush();
1801    }
1802}
1803