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