DataConnection.java revision fcc57e87d1620ab7dc877d65e7e85dca77132054
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.text.TextUtils;
42import android.telephony.Rlog;
43import android.telephony.TelephonyManager;
44import android.util.Patterns;
45import android.util.TimeUtils;
46
47import java.io.FileDescriptor;
48import java.io.PrintWriter;
49import java.util.ArrayList;
50import java.util.List;
51import java.util.concurrent.atomic.AtomicInteger;
52
53/**
54 * {@hide}
55 *
56 * DataConnection StateMachine.
57 *
58 * This a class for representing a single data connection, with instances of this
59 * class representing a connection via the cellular network. There may be multiple
60 * data connections and all of them are managed by the <code>DataConnectionTracker</code>.
61 *
62 * A recent change is to move retry handling into this class, with that change the
63 * old retry manager is now used internally rather than exposed to the DCT. Also,
64 * bringUp now has an initialRetry which is used limit the number of retries
65 * during the initial bring up of the connection. After the connection becomes active
66 * the current max retry is restored to the configured value.
67 *
68 * NOTE: All DataConnection objects must be running on the same looper, which is the default
69 * as the coordinator has members which are used without synchronization.
70 */
71public final class DataConnection extends StateMachine {
72    private static final boolean DBG = true;
73    private static final boolean VDBG = true;
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 DcTrackerBase 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 = DcAsyncChannel.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            DcTrackerBase 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                DcTrackerBase 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 (Build.IS_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     * Initialize connection, this will fail if the
720     * apnSettings are not compatible.
721     *
722     * @param cp the Connection paramemters
723     * @return true if initialization was successful.
724     */
725    private boolean initConnection(ConnectionParams cp) {
726        ApnContext apnContext = cp.mApnContext;
727        if (mApnSetting == null) {
728            // Only change apn setting if it isn't set, it will
729            // only NOT be set only if we're in DcInactiveState.
730            mApnSetting = apnContext.getApnSetting();
731        } else if (mApnSetting.canHandleType(apnContext.getApnType())) {
732            // All is good.
733        } else {
734            if (DBG) {
735                log("initConnection: incompatible apnSetting in ConnectionParams cp=" + cp
736                        + " dc=" + DataConnection.this);
737            }
738            return false;
739        }
740        mTag += 1;
741        mConnectionParams = cp;
742        mConnectionParams.mTag = mTag;
743
744        if (!mApnContexts.contains(apnContext)) {
745            mApnContexts.add(apnContext);
746        }
747        configureRetry(mApnSetting.canHandleType(PhoneConstants.APN_TYPE_DEFAULT));
748        mRetryManager.setRetryCount(0);
749        mRetryManager.setCurMaxRetryCount(mConnectionParams.mInitialMaxRetry);
750        mRetryManager.setRetryForever(false);
751
752        if (DBG) {
753            log("initConnection: "
754                    + " RefCount=" + mApnContexts.size()
755                    + " mApnList=" + mApnContexts
756                    + " mConnectionParams=" + mConnectionParams);
757        }
758        return true;
759    }
760
761    /**
762     * The parent state for all other states.
763     */
764    private class DcDefaultState extends State {
765        @Override
766        public void enter() {
767            if (DBG) log("DcDefaultState: enter");
768
769            // Add ourselves to the list of data connections
770            mDcController.addDc(DataConnection.this);
771        }
772        @Override
773        public void exit() {
774            if (DBG) log("DcDefaultState: exit");
775
776
777            if (mAc != null) {
778                mAc.disconnected();
779                mAc = null;
780            }
781            mDcRetryAlarmController.dispose();
782            mDcRetryAlarmController = null;
783            mApnContexts = null;
784            mReconnectIntent = null;
785            mDct = null;
786            mApnSetting = null;
787            mPhone = null;
788            mLinkProperties = null;
789            mLinkCapabilities = null;
790            mLastFailCause = null;
791            mUserData = null;
792
793            // Remove ourselves from the DC lists
794            mDcController.removeDc(DataConnection.this);
795
796            mDcController = null;
797            mDcTesterFailBringUpAll = null;
798        }
799
800        @Override
801        public boolean processMessage(Message msg) {
802            boolean retVal = HANDLED;
803            AsyncResult ar;
804
805            if (VDBG) {
806                log("DcDefault msg=" + getWhatToString(msg.what)
807                        + " RefCount=" + mApnContexts.size());
808            }
809            switch (msg.what) {
810                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
811                    if (mAc != null) {
812                        if (VDBG) log("Disconnecting to previous connection mAc=" + mAc);
813                        mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
814                                AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
815                    } else {
816                        mAc = new AsyncChannel();
817                        mAc.connected(null, getHandler(), msg.replyTo);
818                        if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected");
819                        mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
820                                AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
821                    }
822                    break;
823                }
824                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
825                    if (VDBG) log("CMD_CHANNEL_DISCONNECTED");
826                    quit();
827                    break;
828                }
829                case DcAsyncChannel.REQ_IS_INACTIVE: {
830                    boolean val = getIsInactive();
831                    if (VDBG) log("REQ_IS_INACTIVE  isInactive=" + val);
832                    mAc.replyToMessage(msg, DcAsyncChannel.RSP_IS_INACTIVE, val ? 1 : 0);
833                    break;
834                }
835                case DcAsyncChannel.REQ_GET_CID: {
836                    int cid = getCid();
837                    if (VDBG) log("REQ_GET_CID  cid=" + cid);
838                    mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_CID, cid);
839                    break;
840                }
841                case DcAsyncChannel.REQ_GET_APNSETTING: {
842                    ApnSetting apnSetting = getApnSetting();
843                    if (VDBG) log("REQ_GET_APNSETTING  mApnSetting=" + apnSetting);
844                    mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_APNSETTING, apnSetting);
845                    break;
846                }
847                case DcAsyncChannel.REQ_GET_LINK_PROPERTIES: {
848                    LinkProperties lp = getCopyLinkProperties();
849                    if (VDBG) log("REQ_GET_LINK_PROPERTIES linkProperties" + lp);
850                    mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_LINK_PROPERTIES, lp);
851                    break;
852                }
853                case DcAsyncChannel.REQ_SET_LINK_PROPERTIES_HTTP_PROXY: {
854                    ProxyProperties proxy = (ProxyProperties) msg.obj;
855                    if (VDBG) log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy);
856                    setLinkPropertiesHttpProxy(proxy);
857                    mAc.replyToMessage(msg, DcAsyncChannel.RSP_SET_LINK_PROPERTIES_HTTP_PROXY);
858                    break;
859                }
860                case DcAsyncChannel.REQ_GET_LINK_CAPABILITIES: {
861                    LinkCapabilities lc = getCopyLinkCapabilities();
862                    if (VDBG) log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
863                    mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_LINK_CAPABILITIES, lc);
864                    break;
865                }
866                case DcAsyncChannel.REQ_RESET:
867                    if (VDBG) log("DcDefaultState: msg.what=REQ_RESET");
868                    transitionTo(mInactiveState);
869                    break;
870                case EVENT_CONNECT:
871                    if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
872                    ConnectionParams cp = (ConnectionParams) msg.obj;
873                    notifyConnectCompleted(cp, DcFailCause.UNKNOWN, false);
874                    break;
875
876                case EVENT_DISCONNECT:
877                    if (DBG) {
878                        log("DcDefaultState deferring msg.what=EVENT_DISCONNECT RefCount="
879                                + mApnContexts.size());
880                    }
881                    deferMessage(msg);
882                    break;
883
884                case EVENT_DISCONNECT_ALL:
885                    if (DBG) {
886                        log("DcDefaultState deferring msg.what=EVENT_DISCONNECT_ALL RefCount="
887                                + mApnContexts.size());
888                    }
889                    deferMessage(msg);
890                    break;
891
892                case EVENT_TEAR_DOWN_NOW:
893                    if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW");
894                    mPhone.mCi.deactivateDataCall(mCid, 0,  null);
895                    break;
896
897                case EVENT_LOST_CONNECTION:
898                    if (DBG) {
899                        String s = "DcDefaultState ignore EVENT_LOST_CONNECTION"
900                            + " tag=" + msg.arg1 + ":mTag=" + mTag;
901                        logAndAddLogRec(s);
902                    }
903                    break;
904
905                case EVENT_RETRY_CONNECTION:
906                    if (DBG) {
907                        String s = "DcDefaultState ignore EVENT_RETRY_CONNECTION"
908                                + " tag=" + msg.arg1 + ":mTag=" + mTag;
909                        logAndAddLogRec(s);
910                    }
911                    break;
912
913                default:
914                    if (DBG) {
915                        log("DcDefaultState: shouldn't happen but ignore msg.what="
916                                + getWhatToString(msg.what));
917                    }
918                    break;
919            }
920
921            return retVal;
922        }
923    }
924    private DcDefaultState mDefaultState = new DcDefaultState();
925
926    /**
927     * The state machine is inactive and expects a EVENT_CONNECT.
928     */
929    private class DcInactiveState extends State {
930        // Inform all contexts we've failed connecting
931        public void setEnterNotificationParams(ConnectionParams cp, DcFailCause cause) {
932            if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
933            mConnectionParams = cp;
934            mDisconnectParams = null;
935            mDcFailCause = cause;
936        }
937
938        // Inform all contexts we've failed disconnected
939        public void setEnterNotificationParams(DisconnectParams dp) {
940            if (VDBG) log("DcInactiveState: setEnterNoticationParams dp");
941            mConnectionParams = null;
942            mDisconnectParams = dp;
943            mDcFailCause = DcFailCause.NONE;
944        }
945
946        // Inform all contexts of the failure cause
947        public void setEnterNotificationParams(DcFailCause cause) {
948            mConnectionParams = null;
949            mDisconnectParams = null;
950            mDcFailCause = cause;
951        }
952
953        @Override
954        public void enter() {
955            mTag += 1;
956            if (DBG) log("DcInactiveState: enter() mTag=" + mTag);
957
958            if (mConnectionParams != null) {
959                if (DBG) {
960                    log("DcInactiveState: enter notifyConnectCompleted +ALL failCause="
961                            + mDcFailCause);
962                }
963                notifyConnectCompleted(mConnectionParams, mDcFailCause, true);
964            }
965            if (mDisconnectParams != null) {
966                if (DBG) {
967                    log("DcInactiveState: enter notifyDisconnectCompleted +ALL failCause="
968                            + mDcFailCause);
969                }
970                notifyDisconnectCompleted(mDisconnectParams, true);
971            }
972            if (mDisconnectParams == null && mConnectionParams == null && mDcFailCause != null) {
973                if (DBG) {
974                    log("DcInactiveState: enter notifyAllDisconnectCompleted failCause="
975                            + mDcFailCause);
976                }
977                notifyAllDisconnectCompleted(mDcFailCause);
978            }
979
980            // Remove ourselves from cid mapping, before clearSettings
981            mDcController.removeActiveDcByCid(DataConnection.this);
982
983            clearSettings();
984        }
985
986        @Override
987        public void exit() {
988        }
989
990        @Override
991        public boolean processMessage(Message msg) {
992            boolean retVal;
993
994            switch (msg.what) {
995                case DcAsyncChannel.REQ_RESET:
996                    if (DBG) {
997                        log("DcInactiveState: msg.what=RSP_RESET, ignore we're already reset");
998                    }
999                    retVal = HANDLED;
1000                    break;
1001
1002                case EVENT_CONNECT:
1003                    if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT");
1004                    ConnectionParams cp = (ConnectionParams) msg.obj;
1005                    if (initConnection(cp)) {
1006                        onConnect(mConnectionParams);
1007                        transitionTo(mActivatingState);
1008                    } else {
1009                        if (DBG) {
1010                            log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed");
1011                        }
1012                        notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
1013                                false);
1014                    }
1015                    retVal = HANDLED;
1016                    break;
1017
1018                case EVENT_DISCONNECT:
1019                    if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT");
1020                    notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
1021                    retVal = HANDLED;
1022                    break;
1023
1024                case EVENT_DISCONNECT_ALL:
1025                    if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT_ALL");
1026                    notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
1027                    retVal = HANDLED;
1028                    break;
1029
1030                default:
1031                    if (VDBG) {
1032                        log("DcInactiveState nothandled msg.what=" + getWhatToString(msg.what));
1033                    }
1034                    retVal = NOT_HANDLED;
1035                    break;
1036            }
1037            return retVal;
1038        }
1039    }
1040    private DcInactiveState mInactiveState = new DcInactiveState();
1041
1042    /**
1043     * The state machine is retrying and expects a EVENT_RETRY_CONNECTION.
1044     */
1045    private class DcRetryingState extends State {
1046        @Override
1047        public void enter() {
1048            if (DBG) {
1049                log("DcRetryingState: enter() mTag=" + mTag
1050                    + ", call notifyAllOfDisconnectDcRetrying lostConnection");
1051            }
1052
1053            notifyAllOfDisconnectDcRetrying(Phone.REASON_LOST_DATA_CONNECTION);
1054
1055            // Remove ourselves from cid mapping
1056            mDcController.removeActiveDcByCid(DataConnection.this);
1057            mCid = -1;
1058        }
1059
1060        @Override
1061        public boolean processMessage(Message msg) {
1062            boolean retVal;
1063
1064            switch (msg.what) {
1065                case EVENT_RETRY_CONNECTION: {
1066                    if (msg.arg1 == mTag) {
1067                        mRetryManager.increaseRetryCount();
1068                        if (DBG) {
1069                            log("DcRetryingState EVENT_RETRY_CONNECTION"
1070                                    + " RetryCount=" +  mRetryManager.getRetryCount()
1071                                    + " mConnectionParams=" + mConnectionParams);
1072                        }
1073                        onConnect(mConnectionParams);
1074                        transitionTo(mActivatingState);
1075                    } else {
1076                        if (DBG) {
1077                            log("DcRetryingState stale EVENT_RETRY_CONNECTION"
1078                                    + " tag:" + msg.arg1 + " != mTag:" + mTag);
1079                        }
1080                    }
1081                    retVal = HANDLED;
1082                    break;
1083                }
1084                case DcAsyncChannel.REQ_RESET: {
1085                    if (DBG) {
1086                        log("DcRetryingState: msg.what=RSP_RESET, ignore we're already reset");
1087                    }
1088                    mInactiveState.setEnterNotificationParams(mConnectionParams,
1089                            DcFailCause.RESET_BY_FRAMEWORK);
1090                    transitionTo(mInactiveState);
1091                    retVal = HANDLED;
1092                    break;
1093                }
1094                case EVENT_CONNECT: {
1095                    ConnectionParams cp = (ConnectionParams) msg.obj;
1096                    if (DBG) {
1097                        log("DcRetryingState: msg.what=EVENT_CONNECT"
1098                                + " RefCount=" + mApnContexts.size() + " cp=" + cp
1099                                + " mConnectionParams=" + mConnectionParams);
1100                    }
1101                    if (initConnection(cp)) {
1102                        onConnect(mConnectionParams);
1103                        transitionTo(mActivatingState);
1104                    } else {
1105                        if (DBG) {
1106                            log("DcRetryingState: msg.what=EVENT_CONNECT initConnection failed");
1107                        }
1108                        notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
1109                                false);
1110                    }
1111                    retVal = HANDLED;
1112                    break;
1113                }
1114                case EVENT_DISCONNECT: {
1115                    DisconnectParams dp = (DisconnectParams) msg.obj;
1116
1117                    if (mApnContexts.remove(dp.mApnContext) && mApnContexts.size() == 0) {
1118                        if (DBG) {
1119                            log("DcRetryingState msg.what=EVENT_DISCONNECT " + " RefCount="
1120                                    + mApnContexts.size() + " dp=" + dp);
1121                        }
1122                        mInactiveState.setEnterNotificationParams(dp);
1123                        transitionTo(mInactiveState);
1124                    } else {
1125                        if (DBG) log("DcRetryingState: msg.what=EVENT_DISCONNECT");
1126                        notifyDisconnectCompleted(dp, false);
1127                    }
1128                    retVal = HANDLED;
1129                    break;
1130                }
1131                case EVENT_DISCONNECT_ALL: {
1132                    if (DBG) {
1133                        log("DcRetryingState msg.what=EVENT_DISCONNECT/DISCONNECT_ALL "
1134                                + "RefCount=" + mApnContexts.size());
1135                    }
1136                    mInactiveState.setEnterNotificationParams(DcFailCause.LOST_CONNECTION);
1137                    deferMessage(msg);
1138                    transitionTo(mInactiveState);
1139                    retVal = HANDLED;
1140                    break;
1141                }
1142                default: {
1143                    if (VDBG) {
1144                        log("DcRetryingState nothandled msg.what=" + getWhatToString(msg.what));
1145                    }
1146                    retVal = NOT_HANDLED;
1147                    break;
1148                }
1149            }
1150            return retVal;
1151        }
1152    }
1153    private DcRetryingState mRetryingState = new DcRetryingState();
1154
1155    /**
1156     * The state machine is activating a connection.
1157     */
1158    private class DcActivatingState extends State {
1159        @Override
1160        public boolean processMessage(Message msg) {
1161            boolean retVal;
1162            AsyncResult ar;
1163            ConnectionParams cp;
1164
1165            if (DBG) log("DcActivatingState: msg=" + msgToString(msg));
1166            switch (msg.what) {
1167                case EVENT_CONNECT:
1168                    deferMessage(msg);
1169                    retVal = HANDLED;
1170                    break;
1171
1172                case EVENT_SETUP_DATA_CONNECTION_DONE:
1173                    ar = (AsyncResult) msg.obj;
1174                    cp = (ConnectionParams) ar.userObj;
1175
1176                    DataCallResponse.SetupResult result = onSetupConnectionCompleted(ar);
1177                    if (result != DataCallResponse.SetupResult.ERR_Stale) {
1178                        if (mConnectionParams != cp) {
1179                            loge("DcActivatingState: WEIRD mConnectionsParams:"+ mConnectionParams
1180                                    + " != cp:" + cp);
1181                        }
1182                    }
1183                    if (DBG) {
1184                        log("DcActivatingState onSetupConnectionCompleted result=" + result
1185                                + " dc=" + DataConnection.this);
1186                    }
1187                    switch (result) {
1188                        case SUCCESS:
1189                            // All is well
1190                            mDcFailCause = DcFailCause.NONE;
1191                            transitionTo(mActiveState);
1192                            break;
1193                        case ERR_BadCommand:
1194                            // Vendor ril rejected the command and didn't connect.
1195                            // Transition to inactive but send notifications after
1196                            // we've entered the mInactive state.
1197                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
1198                            transitionTo(mInactiveState);
1199                            break;
1200                        case ERR_UnacceptableParameter:
1201                            // The addresses given from the RIL are bad
1202                            tearDownData(cp);
1203                            transitionTo(mDisconnectingErrorCreatingConnection);
1204                            break;
1205                        case ERR_GetLastErrorFromRil:
1206                            // Request failed and this is an old RIL
1207                            mPhone.mCi.getLastDataCallFailCause(
1208                                    obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp));
1209                            break;
1210                        case ERR_RilError:
1211                            int delay = mDcRetryAlarmController.getSuggestedRetryTime(
1212                                                                    DataConnection.this, ar);
1213                            if (DBG) {
1214                                log("DcActivatingState: ERR_RilError "
1215                                        + " delay=" + delay
1216                                        + " isRetryNeeded=" + mRetryManager.isRetryNeeded());
1217                            }
1218                            if (delay >= 0) {
1219                                mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION,
1220                                                            mTag, delay);
1221                                transitionTo(mRetryingState);
1222                            } else {
1223                                mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
1224                                transitionTo(mInactiveState);
1225                            }
1226                            break;
1227                        case ERR_Stale:
1228                            loge("DcActivatingState: stale EVENT_SETUP_DATA_CONNECTION_DONE"
1229                                    + " tag:" + cp.mTag + " != mTag:" + mTag);
1230                            break;
1231                        default:
1232                            throw new RuntimeException("Unknown SetupResult, should not happen");
1233                    }
1234                    retVal = HANDLED;
1235                    break;
1236
1237                case EVENT_GET_LAST_FAIL_DONE:
1238                    ar = (AsyncResult) msg.obj;
1239                    cp = (ConnectionParams) ar.userObj;
1240                    if (cp.mTag == mTag) {
1241                        if (mConnectionParams != cp) {
1242                            loge("DcActivatingState: WEIRD mConnectionsParams:" + mConnectionParams
1243                                    + " != cp:" + cp);
1244                        }
1245
1246                        DcFailCause cause = DcFailCause.UNKNOWN;
1247
1248                        if (ar.exception == null) {
1249                            int rilFailCause = ((int[]) (ar.result))[0];
1250                            cause = DcFailCause.fromInt(rilFailCause);
1251                        }
1252                        mDcFailCause = cause;
1253
1254                        int retryDelay = mRetryManager.getRetryTimer();
1255                        if (DBG) {
1256                            log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE"
1257                                    + " cause=" + cause
1258                                    + " retryDelay=" + retryDelay
1259                                    + " isRetryNeeded=" + mRetryManager.isRetryNeeded()
1260                                    + " dc=" + DataConnection.this);
1261                        }
1262                        if (mRetryManager.isRetryNeeded()) {
1263                            mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag,
1264                                                            retryDelay);
1265                            transitionTo(mRetryingState);
1266                        } else {
1267                            // Transition to inactive but send notifications after
1268                            // we've entered the mInactive state.
1269                            mInactiveState.setEnterNotificationParams(cp, cause);
1270                            transitionTo(mInactiveState);
1271                        }
1272                    } else {
1273                        loge("DcActivatingState: stale EVENT_GET_LAST_FAIL_DONE"
1274                                + " tag:" + cp.mTag + " != mTag:" + mTag);
1275                    }
1276
1277                    retVal = HANDLED;
1278                    break;
1279
1280                default:
1281                    if (VDBG) {
1282                        log("DcActivatingState not handled msg.what=" +
1283                                getWhatToString(msg.what) + " RefCount=" + mApnContexts.size());
1284                    }
1285                    retVal = NOT_HANDLED;
1286                    break;
1287            }
1288            return retVal;
1289        }
1290    }
1291    private DcActivatingState mActivatingState = new DcActivatingState();
1292
1293    /**
1294     * The state machine is connected, expecting an EVENT_DISCONNECT.
1295     */
1296    private class DcActiveState extends State {
1297        @Override public void enter() {
1298            if (DBG) log("DcActiveState: enter dc=" + DataConnection.this);
1299
1300            if (mRetryManager.getRetryCount() != 0) {
1301                log("DcActiveState: connected after retrying call notifyAllOfConnected");
1302                mRetryManager.setRetryCount(0);
1303            }
1304            // If we were retrying there maybe more than one, otherwise they'll only be one.
1305            notifyAllOfConnected(Phone.REASON_CONNECTED);
1306
1307            // If the EVENT_CONNECT set the current max retry restore it here
1308            // if it didn't then this is effectively a NOP.
1309            mRetryManager.restoreCurMaxRetryCount();
1310            mDcController.addActiveDcByCid(DataConnection.this);
1311        }
1312
1313        @Override
1314        public void exit() {
1315            if (DBG) log("DcActiveState: exit dc=" + this);
1316        }
1317
1318        @Override
1319        public boolean processMessage(Message msg) {
1320            boolean retVal;
1321
1322            switch (msg.what) {
1323                case EVENT_CONNECT: {
1324                    ConnectionParams cp = (ConnectionParams) msg.obj;
1325                    if (DBG) {
1326                        log("DcActiveState: EVENT_CONNECT cp=" + cp + " dc=" + DataConnection.this);
1327                    }
1328                    if (mApnContexts.contains(cp.mApnContext)) {
1329                        log("DcActiveState ERROR already added apnContext=" + cp.mApnContext);
1330                    } else {
1331                        mApnContexts.add(cp.mApnContext);
1332                        if (DBG) {
1333                            log("DcActiveState msg.what=EVENT_CONNECT RefCount="
1334                                    + mApnContexts.size());
1335                        }
1336                    }
1337                    notifyConnectCompleted(cp, DcFailCause.NONE, false);
1338                    retVal = HANDLED;
1339                    break;
1340                }
1341                case EVENT_DISCONNECT: {
1342                    DisconnectParams dp = (DisconnectParams) msg.obj;
1343                    if (DBG) {
1344                        log("DcActiveState: EVENT_DISCONNECT dp=" + dp
1345                                + " dc=" + DataConnection.this);
1346                    }
1347                    if (mApnContexts.contains(dp.mApnContext)) {
1348                        if (DBG) {
1349                            log("DcActiveState msg.what=EVENT_DISCONNECT RefCount="
1350                                    + mApnContexts.size());
1351                        }
1352
1353                        if (mApnContexts.size() == 1) {
1354                            mApnContexts.clear();
1355                            mDisconnectParams = dp;
1356                            mConnectionParams = null;
1357                            dp.mTag = mTag;
1358                            tearDownData(dp);
1359                            transitionTo(mDisconnectingState);
1360                        } else {
1361                            mApnContexts.remove(dp.mApnContext);
1362                            notifyDisconnectCompleted(dp, false);
1363                        }
1364                    } else {
1365                        log("DcActiveState ERROR no such apnContext=" + dp.mApnContext
1366                                + " in this dc=" + DataConnection.this);
1367                        notifyDisconnectCompleted(dp, false);
1368                    }
1369                    retVal = HANDLED;
1370                    break;
1371                }
1372                case EVENT_DISCONNECT_ALL: {
1373                    if (DBG) {
1374                        log("DcActiveState EVENT_DISCONNECT clearing apn contexts,"
1375                                + " dc=" + DataConnection.this);
1376                    }
1377                    mApnContexts.clear();
1378                    DisconnectParams dp = (DisconnectParams) msg.obj;
1379                    mDisconnectParams = dp;
1380                    mConnectionParams = null;
1381                    dp.mTag = mTag;
1382                    tearDownData(dp);
1383                    transitionTo(mDisconnectingState);
1384                    retVal = HANDLED;
1385                    break;
1386                }
1387                case EVENT_LOST_CONNECTION: {
1388                    if (DBG) {
1389                        log("DcActiveState EVENT_LOST_CONNECTION dc=" + DataConnection.this);
1390                    }
1391                    if (mRetryManager.isRetryNeeded()) {
1392                        // We're going to retry
1393                        int delayMillis = mRetryManager.getRetryTimer();
1394                        if (DBG) {
1395                            log("DcActiveState EVENT_LOST_CONNECTION startRetryAlarm"
1396                                    + " mTag=" + mTag + " delay=" + delayMillis + "ms");
1397                        }
1398                        mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag,
1399                                delayMillis);
1400                        transitionTo(mRetryingState);
1401                    } else {
1402                        mInactiveState.setEnterNotificationParams(DcFailCause.LOST_CONNECTION);
1403                        transitionTo(mInactiveState);
1404                    }
1405                    retVal = HANDLED;
1406                    break;
1407                }
1408                default:
1409                    if (VDBG) {
1410                        log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
1411                    }
1412                    retVal = NOT_HANDLED;
1413                    break;
1414            }
1415            return retVal;
1416        }
1417    }
1418    private DcActiveState mActiveState = new DcActiveState();
1419
1420    /**
1421     * The state machine is disconnecting.
1422     */
1423    private class DcDisconnectingState extends State {
1424        @Override
1425        public boolean processMessage(Message msg) {
1426            boolean retVal;
1427
1428            switch (msg.what) {
1429                case EVENT_CONNECT:
1430                    if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
1431                            + mApnContexts.size());
1432                    deferMessage(msg);
1433                    retVal = HANDLED;
1434                    break;
1435
1436                case EVENT_DEACTIVATE_DONE:
1437                    if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
1438                            + mApnContexts.size());
1439                    AsyncResult ar = (AsyncResult) msg.obj;
1440                    DisconnectParams dp = (DisconnectParams) ar.userObj;
1441                    if (dp.mTag == mTag) {
1442                        // Transition to inactive but send notifications after
1443                        // we've entered the mInactive state.
1444                        mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
1445                        transitionTo(mInactiveState);
1446                    } else {
1447                        if (DBG) log("DcDisconnectState stale EVENT_DEACTIVATE_DONE"
1448                                + " dp.tag=" + dp.mTag + " mTag=" + mTag);
1449                    }
1450                    retVal = HANDLED;
1451                    break;
1452
1453                default:
1454                    if (VDBG) {
1455                        log("DcDisconnectingState not handled msg.what="
1456                                + getWhatToString(msg.what));
1457                    }
1458                    retVal = NOT_HANDLED;
1459                    break;
1460            }
1461            return retVal;
1462        }
1463    }
1464    private DcDisconnectingState mDisconnectingState = new DcDisconnectingState();
1465
1466    /**
1467     * The state machine is disconnecting after an creating a connection.
1468     */
1469    private class DcDisconnectionErrorCreatingConnection extends State {
1470        @Override
1471        public boolean processMessage(Message msg) {
1472            boolean retVal;
1473
1474            switch (msg.what) {
1475                case EVENT_DEACTIVATE_DONE:
1476                    AsyncResult ar = (AsyncResult) msg.obj;
1477                    ConnectionParams cp = (ConnectionParams) ar.userObj;
1478                    if (cp.mTag == mTag) {
1479                        if (DBG) {
1480                            log("DcDisconnectionErrorCreatingConnection" +
1481                                " msg.what=EVENT_DEACTIVATE_DONE");
1482                        }
1483
1484                        // Transition to inactive but send notifications after
1485                        // we've entered the mInactive state.
1486                        mInactiveState.setEnterNotificationParams(cp,
1487                                DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER);
1488                        transitionTo(mInactiveState);
1489                    } else {
1490                        if (DBG) {
1491                            log("DcDisconnectionErrorCreatingConnection stale EVENT_DEACTIVATE_DONE"
1492                                    + " dp.tag=" + cp.mTag + ", mTag=" + mTag);
1493                        }
1494                    }
1495                    retVal = HANDLED;
1496                    break;
1497
1498                default:
1499                    if (VDBG) {
1500                        log("DcDisconnectionErrorCreatingConnection not handled msg.what="
1501                                + getWhatToString(msg.what));
1502                    }
1503                    retVal = NOT_HANDLED;
1504                    break;
1505            }
1506            return retVal;
1507        }
1508    }
1509    private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection =
1510                new DcDisconnectionErrorCreatingConnection();
1511
1512    // ******* "public" interface
1513
1514    /**
1515     * Used for testing purposes.
1516     */
1517    /* package */ void tearDownNow() {
1518        if (DBG) log("tearDownNow()");
1519        sendMessage(obtainMessage(EVENT_TEAR_DOWN_NOW));
1520    }
1521
1522    /**
1523     * @return the string for msg.what as our info.
1524     */
1525    @Override
1526    protected String getWhatToString(int what) {
1527        return cmdToString(what);
1528    }
1529
1530    private static String msgToString(Message msg) {
1531        String retVal;
1532        if (msg == null) {
1533            retVal = "null";
1534        } else {
1535            StringBuilder   b = new StringBuilder();
1536
1537            b.append("{what=");
1538            b.append(cmdToString(msg.what));
1539
1540            b.append(" when=");
1541            TimeUtils.formatDuration(msg.getWhen() - SystemClock.uptimeMillis(), b);
1542
1543            if (msg.arg1 != 0) {
1544                b.append(" arg1=");
1545                b.append(msg.arg1);
1546            }
1547
1548            if (msg.arg2 != 0) {
1549                b.append(" arg2=");
1550                b.append(msg.arg2);
1551            }
1552
1553            if (msg.obj != null) {
1554                b.append(" obj=");
1555                b.append(msg.obj);
1556            }
1557
1558            b.append(" target=");
1559            b.append(msg.getTarget());
1560
1561            b.append(" replyTo=");
1562            b.append(msg.replyTo);
1563
1564            b.append("}");
1565
1566            retVal = b.toString();
1567        }
1568        return retVal;
1569    }
1570
1571    static void slog(String s) {
1572        Rlog.d("DC", s);
1573    }
1574
1575    /**
1576     * Log with debug
1577     *
1578     * @param s is string log
1579     */
1580    @Override
1581    protected void log(String s) {
1582        Rlog.d(getName(), s);
1583    }
1584
1585    /**
1586     * Log with debug attribute
1587     *
1588     * @param s is string log
1589     */
1590    @Override
1591    protected void logd(String s) {
1592        Rlog.d(getName(), s);
1593    }
1594
1595    /**
1596     * Log with verbose attribute
1597     *
1598     * @param s is string log
1599     */
1600    @Override
1601    protected void logv(String s) {
1602        Rlog.v(getName(), s);
1603    }
1604
1605    /**
1606     * Log with info attribute
1607     *
1608     * @param s is string log
1609     */
1610    @Override
1611    protected void logi(String s) {
1612        Rlog.i(getName(), s);
1613    }
1614
1615    /**
1616     * Log with warning attribute
1617     *
1618     * @param s is string log
1619     */
1620    @Override
1621    protected void logw(String s) {
1622        Rlog.w(getName(), s);
1623    }
1624
1625    /**
1626     * Log with error attribute
1627     *
1628     * @param s is string log
1629     */
1630    @Override
1631    protected void loge(String s) {
1632        Rlog.e(getName(), s);
1633    }
1634
1635    /**
1636     * Log with error attribute
1637     *
1638     * @param s is string log
1639     * @param e is a Throwable which logs additional information.
1640     */
1641    @Override
1642    protected void loge(String s, Throwable e) {
1643        Rlog.e(getName(), s, e);
1644    }
1645
1646    /** Doesn't print mApnList of ApnContext's which would be recursive */
1647    public String toStringSimple() {
1648        return getName() + ": State=" + getCurrentState().getName()
1649                + " mApnSetting=" + mApnSetting + " RefCount=" + mApnContexts.size()
1650                + " mCid=" + mCid + " mCreateTime=" + mCreateTime
1651                + " mLastastFailTime=" + mLastFailTime
1652                + " mLastFailCause=" + mLastFailCause
1653                + " mTag=" + mTag
1654                + " mRetryManager=" + mRetryManager
1655                + " mLinkProperties=" + mLinkProperties
1656                + " mLinkCapabilities=" + mLinkCapabilities;
1657    }
1658
1659    @Override
1660    public String toString() {
1661        return "{" + toStringSimple() + " mApnContexts=" + mApnContexts + "}";
1662    }
1663
1664    /**
1665     * Dump the current state.
1666     *
1667     * @param fd
1668     * @param pw
1669     * @param args
1670     */
1671    @Override
1672    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1673        pw.print("DataConnection ");
1674        super.dump(fd, pw, args);
1675        pw.println(" mApnContexts.size=" + mApnContexts.size());
1676        pw.println(" mApnContexts=" + mApnContexts);
1677        pw.flush();
1678        pw.println(" mDataConnectionTracker=" + mDct);
1679        pw.println(" mApnSetting=" + mApnSetting);
1680        pw.println(" mTag=" + mTag);
1681        pw.println(" mCid=" + mCid);
1682        pw.println(" mRetryManager=" + mRetryManager);
1683        pw.println(" mConnectionParams=" + mConnectionParams);
1684        pw.println(" mDisconnectParams=" + mDisconnectParams);
1685        pw.println(" mDcFailCause=" + mDcFailCause);
1686        pw.flush();
1687        pw.println(" mPhone=" + mPhone);
1688        pw.flush();
1689        pw.println(" mLinkProperties=" + mLinkProperties);
1690        pw.flush();
1691        pw.println(" mLinkCapabilities=" + mLinkCapabilities);
1692        pw.println(" mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
1693        pw.println(" mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime));
1694        pw.println(" mLastFailCause=" + mLastFailCause);
1695        pw.flush();
1696        pw.println(" mUserData=" + mUserData);
1697        pw.println(" mInstanceNumber=" + mInstanceNumber);
1698        pw.println(" mAc=" + mAc);
1699        pw.println(" mDcRetryAlarmController=" + mDcRetryAlarmController);
1700        pw.flush();
1701    }
1702}
1703