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