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