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;
18
19
20import com.android.internal.telephony.DataCallState.SetupResult;
21import com.android.internal.util.AsyncChannel;
22import com.android.internal.util.Protocol;
23import com.android.internal.util.State;
24import com.android.internal.util.StateMachine;
25
26import android.app.PendingIntent;
27import android.net.LinkCapabilities;
28import android.net.LinkProperties;
29import android.net.ProxyProperties;
30import android.os.AsyncResult;
31import android.os.Message;
32import android.os.SystemProperties;
33import android.text.TextUtils;
34import android.util.TimeUtils;
35
36import java.io.FileDescriptor;
37import java.io.PrintWriter;
38import java.util.ArrayList;
39import java.util.Calendar;
40import java.util.HashMap;
41import java.util.List;
42import java.util.concurrent.atomic.AtomicInteger;
43
44/**
45 * {@hide}
46 *
47 * DataConnection StateMachine.
48 *
49 * This is an abstract base class for representing a single data connection.
50 * Instances of this class such as <code>CdmaDataConnection</code> and
51 * <code>GsmDataConnection</code>, * represent a connection via the cellular network.
52 * There may be multiple data connections and all of them are managed by the
53 * <code>DataConnectionTracker</code>.
54 *
55 * Instances are asynchronous state machines and have two primary entry points
56 * <code>connect()</code> and <code>disconnect</code>. The message a parameter will be returned
57 * hen the operation completes. The <code>msg.obj</code> will contain an AsyncResult
58 * object and <code>AsyncResult.userObj</code> is the original <code>msg.obj</code>. if successful
59 * with the <code>AsyncResult.result == null</code> and <code>AsyncResult.exception == null</code>.
60 * If an error <code>AsyncResult.result = FailCause</code> and
61 * <code>AsyncResult.exception = new Exception()</code>.
62 *
63 * The other public methods are provided for debugging.
64 */
65public abstract class DataConnection extends StateMachine {
66    protected static final boolean DBG = true;
67    protected static final boolean VDBG = false;
68
69    protected static AtomicInteger mCount = new AtomicInteger(0);
70    protected AsyncChannel mAc;
71
72    protected List<ApnContext> mApnList = null;
73    PendingIntent mReconnectIntent = null;
74
75    private DataConnectionTracker mDataConnectionTracker = null;
76
77    /**
78     * Used internally for saving connecting parameters.
79     */
80    protected static class ConnectionParams {
81        public ConnectionParams(ApnSetting apn, Message onCompletedMsg) {
82            this.apn = apn;
83            this.onCompletedMsg = onCompletedMsg;
84        }
85
86        public int tag;
87        public ApnSetting apn;
88        public Message onCompletedMsg;
89    }
90
91    /**
92     * Used internally for saving disconnecting parameters.
93     */
94    protected static class DisconnectParams {
95        public DisconnectParams(String reason, Message onCompletedMsg) {
96            this.reason = reason;
97            this.onCompletedMsg = onCompletedMsg;
98        }
99        public int tag;
100        public String reason;
101        public Message onCompletedMsg;
102    }
103
104    /**
105     * Returned as the reason for a connection failure as defined
106     * by RIL_DataCallFailCause in ril.h and some local errors.
107     */
108    public enum FailCause {
109        NONE(0),
110
111        // This series of errors as specified by the standards
112        // specified in ril.h
113        OPERATOR_BARRED(0x08),
114        INSUFFICIENT_RESOURCES(0x1A),
115        MISSING_UNKNOWN_APN(0x1B),
116        UNKNOWN_PDP_ADDRESS_TYPE(0x1C),
117        USER_AUTHENTICATION(0x1D),
118        ACTIVATION_REJECT_GGSN(0x1E),
119        ACTIVATION_REJECT_UNSPECIFIED(0x1F),
120        SERVICE_OPTION_NOT_SUPPORTED(0x20),
121        SERVICE_OPTION_NOT_SUBSCRIBED(0x21),
122        SERVICE_OPTION_OUT_OF_ORDER(0x22),
123        NSAPI_IN_USE(0x23),
124        ONLY_IPV4_ALLOWED(0x32),
125        ONLY_IPV6_ALLOWED(0x33),
126        ONLY_SINGLE_BEARER_ALLOWED(0x34),
127        PROTOCOL_ERRORS(0x6F),
128
129        // Local errors generated by Vendor RIL
130        // specified in ril.h
131        REGISTRATION_FAIL(-1),
132        GPRS_REGISTRATION_FAIL(-2),
133        SIGNAL_LOST(-3),
134        PREF_RADIO_TECH_CHANGED(-4),
135        RADIO_POWER_OFF(-5),
136        TETHERED_CALL_ACTIVE(-6),
137        ERROR_UNSPECIFIED(0xFFFF),
138
139        // Errors generated by the Framework
140        // specified here
141        UNKNOWN(0x10000),
142        RADIO_NOT_AVAILABLE(0x10001),
143        UNACCEPTABLE_NETWORK_PARAMETER(0x10002),
144        CONNECTION_TO_DATACONNECTIONAC_BROKEN(0x10003);
145
146        private final int mErrorCode;
147        private static final HashMap<Integer, FailCause> sErrorCodeToFailCauseMap;
148        static {
149            sErrorCodeToFailCauseMap = new HashMap<Integer, FailCause>();
150            for (FailCause fc : values()) {
151                sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc);
152            }
153        }
154
155        FailCause(int errorCode) {
156            mErrorCode = errorCode;
157        }
158
159        int getErrorCode() {
160            return mErrorCode;
161        }
162
163        public boolean isPermanentFail() {
164            return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) ||
165                   (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
166                   (this == SERVICE_OPTION_NOT_SUPPORTED) ||
167                   (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) ||
168                   (this == PROTOCOL_ERRORS);
169        }
170
171        public boolean isEventLoggable() {
172            return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) ||
173                    (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
174                    (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
175                    (this == SERVICE_OPTION_NOT_SUBSCRIBED) ||
176                    (this == SERVICE_OPTION_NOT_SUPPORTED) ||
177                    (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) ||
178                    (this == PROTOCOL_ERRORS) ||
179                    (this == UNACCEPTABLE_NETWORK_PARAMETER);
180        }
181
182        public static FailCause fromInt(int errorCode) {
183            FailCause fc = sErrorCodeToFailCauseMap.get(errorCode);
184            if (fc == null) {
185                fc = UNKNOWN;
186            }
187            return fc;
188        }
189    }
190
191    public static class CallSetupException extends Exception {
192        private int mRetryOverride = -1;
193
194        CallSetupException (int retryOverride) {
195            mRetryOverride = retryOverride;
196        }
197
198        public int getRetryOverride() {
199            return mRetryOverride;
200        }
201    }
202
203    // ***** Event codes for driving the state machine
204    protected static final int BASE = Protocol.BASE_DATA_CONNECTION;
205    protected static final int EVENT_CONNECT = BASE + 0;
206    protected static final int EVENT_SETUP_DATA_CONNECTION_DONE = BASE + 1;
207    protected static final int EVENT_GET_LAST_FAIL_DONE = BASE + 2;
208    protected static final int EVENT_DEACTIVATE_DONE = BASE + 3;
209    protected static final int EVENT_DISCONNECT = BASE + 4;
210    protected static final int EVENT_RIL_CONNECTED = BASE + 5;
211    protected static final int EVENT_DISCONNECT_ALL = BASE + 6;
212
213    private static final int CMD_TO_STRING_COUNT = EVENT_DISCONNECT_ALL - BASE + 1;
214    private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
215    static {
216        sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT";
217        sCmdToString[EVENT_SETUP_DATA_CONNECTION_DONE - BASE] =
218                "EVENT_SETUP_DATA_CONNECTION_DONE";
219        sCmdToString[EVENT_GET_LAST_FAIL_DONE - BASE] = "EVENT_GET_LAST_FAIL_DONE";
220        sCmdToString[EVENT_DEACTIVATE_DONE - BASE] = "EVENT_DEACTIVATE_DONE";
221        sCmdToString[EVENT_DISCONNECT - BASE] = "EVENT_DISCONNECT";
222        sCmdToString[EVENT_RIL_CONNECTED - BASE] = "EVENT_RIL_CONNECTED";
223        sCmdToString[EVENT_DISCONNECT_ALL - BASE] = "EVENT_DISCONNECT_ALL";
224    }
225    protected static String cmdToString(int cmd) {
226        cmd -= BASE;
227        if ((cmd >= 0) && (cmd < sCmdToString.length)) {
228            return sCmdToString[cmd];
229        } else {
230            return null;
231        }
232    }
233
234    //***** Tag IDs for EventLog
235    protected static final int EVENT_LOG_BAD_DNS_ADDRESS = 50100;
236
237    //***** Member Variables
238    protected ApnSetting mApn;
239    protected int mTag;
240    protected PhoneBase phone;
241    protected int mRilVersion = -1;
242    protected int cid;
243    protected LinkProperties mLinkProperties = new LinkProperties();
244    protected LinkCapabilities mCapabilities = new LinkCapabilities();
245    protected long createTime;
246    protected long lastFailTime;
247    protected FailCause lastFailCause;
248    protected int mRetryOverride = -1;
249    protected static final String NULL_IP = "0.0.0.0";
250    protected int mRefCount;
251    Object userData;
252
253    //***** Abstract methods
254    @Override
255    public abstract String toString();
256
257    protected abstract void onConnect(ConnectionParams cp);
258
259    protected abstract boolean isDnsOk(String[] domainNameServers);
260
261    protected abstract void log(String s);
262
263   //***** Constructor
264    protected DataConnection(PhoneBase phone, String name, int id, RetryManager rm,
265            DataConnectionTracker dct) {
266        super(name);
267        setLogRecSize(100);
268        if (DBG) log("DataConnection constructor E");
269        this.phone = phone;
270        this.mDataConnectionTracker = dct;
271        mId = id;
272        mRetryMgr = rm;
273        this.cid = -1;
274
275        setDbg(false);
276        addState(mDefaultState);
277            addState(mInactiveState, mDefaultState);
278            addState(mActivatingState, mDefaultState);
279            addState(mActiveState, mDefaultState);
280            addState(mDisconnectingState, mDefaultState);
281            addState(mDisconnectingErrorCreatingConnection, mDefaultState);
282        setInitialState(mInactiveState);
283
284        mApnList = new ArrayList<ApnContext>();
285        if (DBG) log("DataConnection constructor X");
286    }
287
288    /**
289     * Shut down this instance and its state machine.
290     */
291    private void shutDown() {
292        if (DBG) log("shutDown");
293
294        if (mAc != null) {
295            mAc.disconnected();
296            mAc = null;
297        }
298        mApnList = null;
299        mReconnectIntent = null;
300        mDataConnectionTracker = null;
301        mApn = null;
302        phone = null;
303        mLinkProperties = null;
304        mCapabilities = null;
305        lastFailCause = null;
306        userData = null;
307    }
308
309    /**
310     * TearDown the data connection.
311     *
312     * @param o will be returned in AsyncResult.userObj
313     *          and is either a DisconnectParams or ConnectionParams.
314     */
315    private void tearDownData(Object o) {
316        int discReason = RILConstants.DEACTIVATE_REASON_NONE;
317        if ((o != null) && (o instanceof DisconnectParams)) {
318            DisconnectParams dp = (DisconnectParams)o;
319            Message m = dp.onCompletedMsg;
320            if (TextUtils.equals(dp.reason, Phone.REASON_RADIO_TURNED_OFF)) {
321                discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
322            } else if (TextUtils.equals(dp.reason, Phone.REASON_PDP_RESET)) {
323                discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
324            }
325        }
326        if (phone.mCM.getRadioState().isOn()) {
327            if (DBG) log("tearDownData radio is on, call deactivateDataCall");
328            phone.mCM.deactivateDataCall(cid, discReason, obtainMessage(EVENT_DEACTIVATE_DONE, o));
329        } else {
330            if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately");
331            AsyncResult ar = new AsyncResult(o, null, null);
332            sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, ar));
333        }
334    }
335
336    /**
337     * Send the connectionCompletedMsg.
338     *
339     * @param cp is the ConnectionParams
340     * @param cause
341     */
342    private void notifyConnectCompleted(ConnectionParams cp, FailCause cause) {
343        Message connectionCompletedMsg = cp.onCompletedMsg;
344        if (connectionCompletedMsg == null) {
345            return;
346        }
347
348        long timeStamp = System.currentTimeMillis();
349        connectionCompletedMsg.arg1 = cid;
350
351        if (cause == FailCause.NONE) {
352            createTime = timeStamp;
353            AsyncResult.forMessage(connectionCompletedMsg);
354        } else {
355            lastFailCause = cause;
356            lastFailTime = timeStamp;
357            AsyncResult.forMessage(connectionCompletedMsg, cause,
358                                   new CallSetupException(mRetryOverride));
359        }
360        if (DBG) log("notifyConnectionCompleted at " + timeStamp + " cause=" + cause);
361
362        connectionCompletedMsg.sendToTarget();
363    }
364
365    /**
366     * Send ar.userObj if its a message, which is should be back to originator.
367     *
368     * @param dp is the DisconnectParams.
369     */
370    private void notifyDisconnectCompleted(DisconnectParams dp, boolean sendAll) {
371        if (VDBG) log("NotifyDisconnectCompleted");
372
373        ApnContext alreadySent = null;
374        String reason = null;
375
376        if (dp.onCompletedMsg != null) {
377            // Get ApnContext, but only valid on GSM devices this is a string on CDMA devices.
378            Message msg = dp.onCompletedMsg;
379            if (msg.obj instanceof ApnContext) {
380                alreadySent = (ApnContext)msg.obj;
381            }
382            reason = dp.reason;
383            if (VDBG) {
384                log(String.format("msg=%s msg.obj=%s", msg.toString(),
385                    ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
386            }
387            AsyncResult.forMessage(msg);
388            msg.sendToTarget();
389        }
390        if (sendAll) {
391            for (ApnContext a : mApnList) {
392                if (a == alreadySent) continue;
393                if (reason != null) a.setReason(reason);
394                Message msg = mDataConnectionTracker.obtainMessage(
395                        DctConstants.EVENT_DISCONNECT_DONE, a);
396                AsyncResult.forMessage(msg);
397                msg.sendToTarget();
398            }
399        }
400
401        if (DBG) log("NotifyDisconnectCompleted DisconnectParams=" + dp);
402    }
403
404    protected int getRilRadioTechnology(int defaultRilRadioTechnology) {
405        int rilRadioTechnology;
406        if (mRilVersion < 6) {
407            rilRadioTechnology = defaultRilRadioTechnology;
408        } else {
409            rilRadioTechnology = phone.getServiceState().getRilRadioTechnology() + 2;
410        }
411        return rilRadioTechnology;
412    }
413
414    /*
415     * **************************************************************************
416     * Begin Members and methods owned by DataConnectionTracker but stored
417     * in a DataConnection because there is one per connection.
418     * **************************************************************************
419     */
420
421    /*
422     * The id is owned by DataConnectionTracker.
423     */
424    private int mId;
425
426    /**
427     * Get the DataConnection ID
428     */
429    public int getDataConnectionId() {
430        return mId;
431    }
432
433    /*
434     * The retry manager is currently owned by the DataConnectionTracker but is stored
435     * in the DataConnection because there is one per connection. These methods
436     * should only be used by the DataConnectionTracker although someday the retrying
437     * maybe managed by the DataConnection itself and these methods could disappear.
438     */
439    private RetryManager mRetryMgr;
440
441    /**
442     * @return retry manager retryCount
443     */
444    public int getRetryCount() {
445        return mRetryMgr.getRetryCount();
446    }
447
448    /**
449     * set retry manager retryCount
450     */
451    public void setRetryCount(int retryCount) {
452        if (DBG) log("setRetryCount: " + retryCount);
453        mRetryMgr.setRetryCount(retryCount);
454    }
455
456    /**
457     * @return retry manager retryTimer
458     */
459    public int getRetryTimer() {
460        return mRetryMgr.getRetryTimer();
461    }
462
463    /**
464     * increaseRetryCount of retry manager
465     */
466    public void increaseRetryCount() {
467        mRetryMgr.increaseRetryCount();
468    }
469
470    /**
471     * @return retry manager isRetryNeeded
472     */
473    public boolean isRetryNeeded() {
474        return mRetryMgr.isRetryNeeded();
475    }
476
477    /**
478     * resetRetryCount of retry manager
479     */
480    public void resetRetryCount() {
481        mRetryMgr.resetRetryCount();
482    }
483
484    /**
485     * set retryForeverUsingLasttimeout of retry manager
486     */
487    public void retryForeverUsingLastTimeout() {
488        mRetryMgr.retryForeverUsingLastTimeout();
489    }
490
491    /**
492     * @return retry manager isRetryForever
493     */
494    public boolean isRetryForever() {
495        return mRetryMgr.isRetryForever();
496    }
497
498    /**
499     * @return whether the retry config is set successfully or not
500     */
501    public boolean configureRetry(int maxRetryCount, int retryTime, int randomizationTime) {
502        return mRetryMgr.configure(maxRetryCount, retryTime, randomizationTime);
503    }
504
505    /**
506     * @return whether the retry config is set successfully or not
507     */
508    public boolean configureRetry(String configStr) {
509        return mRetryMgr.configure(configStr);
510    }
511
512    /*
513     * **************************************************************************
514     * End members owned by DataConnectionTracker
515     * **************************************************************************
516     */
517
518    /**
519     * Clear all settings called when entering mInactiveState.
520     */
521    protected void clearSettings() {
522        if (DBG) log("clearSettings");
523
524        createTime = -1;
525        lastFailTime = -1;
526        lastFailCause = FailCause.NONE;
527        mRetryOverride = -1;
528        mRefCount = 0;
529        cid = -1;
530
531        mLinkProperties = new LinkProperties();
532        mApn = null;
533    }
534
535    /**
536     * Process setup completion.
537     *
538     * @param ar is the result
539     * @return SetupResult.
540     */
541    private DataCallState.SetupResult onSetupConnectionCompleted(AsyncResult ar) {
542        DataCallState response = (DataCallState) ar.result;
543        ConnectionParams cp = (ConnectionParams) ar.userObj;
544        DataCallState.SetupResult result;
545
546        if (ar.exception != null) {
547            if (DBG) {
548                log("onSetupConnectionCompleted failed, ar.exception=" + ar.exception +
549                    " response=" + response);
550            }
551
552            if (ar.exception instanceof CommandException
553                    && ((CommandException) (ar.exception)).getCommandError()
554                    == CommandException.Error.RADIO_NOT_AVAILABLE) {
555                result = DataCallState.SetupResult.ERR_BadCommand;
556                result.mFailCause = FailCause.RADIO_NOT_AVAILABLE;
557            } else if ((response == null) || (response.version < 4)) {
558                result = DataCallState.SetupResult.ERR_GetLastErrorFromRil;
559            } else {
560                result = DataCallState.SetupResult.ERR_RilError;
561                result.mFailCause = FailCause.fromInt(response.status);
562            }
563        } else if (cp.tag != mTag) {
564            if (DBG) {
565                log("BUG: onSetupConnectionCompleted is stale cp.tag=" + cp.tag + ", mtag=" + mTag);
566            }
567            result = DataCallState.SetupResult.ERR_Stale;
568        } else if (response.status != 0) {
569            result = DataCallState.SetupResult.ERR_RilError;
570            result.mFailCause = FailCause.fromInt(response.status);
571        } else {
572            if (DBG) log("onSetupConnectionCompleted received DataCallState: " + response);
573            cid = response.cid;
574            result = updateLinkProperty(response).setupResult;
575        }
576
577        return result;
578    }
579
580    private int getSuggestedRetryTime(AsyncResult ar) {
581        int retry = -1;
582        if (ar.exception == null) {
583            DataCallState response = (DataCallState) ar.result;
584            retry =  response.suggestedRetryTime;
585        }
586        return retry;
587    }
588
589    private DataCallState.SetupResult setLinkProperties(DataCallState response,
590            LinkProperties lp) {
591        // Check if system property dns usable
592        boolean okToUseSystemPropertyDns = false;
593        String propertyPrefix = "net." + response.ifname + ".";
594        String dnsServers[] = new String[2];
595        dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1");
596        dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
597        okToUseSystemPropertyDns = isDnsOk(dnsServers);
598
599        // set link properties based on data call response
600        return response.setLinkProperties(lp, okToUseSystemPropertyDns);
601    }
602
603    public static class UpdateLinkPropertyResult {
604        public DataCallState.SetupResult setupResult = DataCallState.SetupResult.SUCCESS;
605        public LinkProperties oldLp;
606        public LinkProperties newLp;
607        public UpdateLinkPropertyResult(LinkProperties curLp) {
608            oldLp = curLp;
609            newLp = curLp;
610        }
611    }
612
613    private UpdateLinkPropertyResult updateLinkProperty(DataCallState newState) {
614        UpdateLinkPropertyResult result = new UpdateLinkPropertyResult(mLinkProperties);
615
616        if (newState == null) return result;
617
618        DataCallState.SetupResult setupResult;
619        result.newLp = new LinkProperties();
620
621        // set link properties based on data call response
622        result.setupResult = setLinkProperties(newState, result.newLp);
623        if (result.setupResult != DataCallState.SetupResult.SUCCESS) {
624            if (DBG) log("updateLinkProperty failed : " + result.setupResult);
625            return result;
626        }
627        // copy HTTP proxy as it is not part DataCallState.
628        result.newLp.setHttpProxy(mLinkProperties.getHttpProxy());
629
630        if (DBG && (! result.oldLp.equals(result.newLp))) {
631            log("updateLinkProperty old LP=" + result.oldLp);
632            log("updateLinkProperty new LP=" + result.newLp);
633        }
634        mLinkProperties = result.newLp;
635
636        return result;
637    }
638
639    /**
640     * The parent state for all other states.
641     */
642    private class DcDefaultState extends State {
643        @Override
644        public void enter() {
645            phone.mCM.registerForRilConnected(getHandler(), EVENT_RIL_CONNECTED, null);
646        }
647        @Override
648        public void exit() {
649            phone.mCM.unregisterForRilConnected(getHandler());
650            shutDown();
651        }
652        @Override
653        public boolean processMessage(Message msg) {
654            boolean retVal = HANDLED;
655            AsyncResult ar;
656
657            switch (msg.what) {
658                case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
659                    if (mAc != null) {
660                        if (VDBG) log("Disconnecting to previous connection mAc=" + mAc);
661                        mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
662                                AsyncChannel.STATUS_FULL_CONNECTION_REFUSED_ALREADY_CONNECTED);
663                    } else {
664                        mAc = new AsyncChannel();
665                        mAc.connected(null, getHandler(), msg.replyTo);
666                        if (VDBG) log("DcDefaultState: FULL_CONNECTION reply connected");
667                        mAc.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
668                                AsyncChannel.STATUS_SUCCESSFUL, mId, "hi");
669                    }
670                    break;
671                }
672                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
673                    if (VDBG) log("CMD_CHANNEL_DISCONNECTED");
674                    quit();
675                    break;
676                }
677                case DataConnectionAc.REQ_IS_INACTIVE: {
678                    boolean val = getCurrentState() == mInactiveState;
679                    if (VDBG) log("REQ_IS_INACTIVE  isInactive=" + val);
680                    mAc.replyToMessage(msg, DataConnectionAc.RSP_IS_INACTIVE, val ? 1 : 0);
681                    break;
682                }
683                case DataConnectionAc.REQ_GET_CID: {
684                    if (VDBG) log("REQ_GET_CID  cid=" + cid);
685                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_CID, cid);
686                    break;
687                }
688                case DataConnectionAc.REQ_GET_APNSETTING: {
689                    if (VDBG) log("REQ_GET_APNSETTING  apnSetting=" + mApn);
690                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNSETTING, mApn);
691                    break;
692                }
693                case DataConnectionAc.REQ_GET_LINK_PROPERTIES: {
694                    LinkProperties lp = new LinkProperties(mLinkProperties);
695                    if (VDBG) log("REQ_GET_LINK_PROPERTIES linkProperties" + lp);
696                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_PROPERTIES, lp);
697                    break;
698                }
699                case DataConnectionAc.REQ_SET_LINK_PROPERTIES_HTTP_PROXY: {
700                    ProxyProperties proxy = (ProxyProperties) msg.obj;
701                    if (VDBG) log("REQ_SET_LINK_PROPERTIES_HTTP_PROXY proxy=" + proxy);
702                    mLinkProperties.setHttpProxy(proxy);
703                    mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_LINK_PROPERTIES_HTTP_PROXY);
704                    break;
705                }
706                case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: {
707                    DataCallState newState = (DataCallState) msg.obj;
708                    UpdateLinkPropertyResult result =
709                                             updateLinkProperty(newState);
710                    if (VDBG) {
711                        log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE result="
712                            + result + " newState=" + newState);
713                    }
714                    mAc.replyToMessage(msg,
715                                   DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE,
716                                   result);
717                    break;
718                }
719                case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: {
720                    LinkCapabilities lc = new LinkCapabilities(mCapabilities);
721                    if (VDBG) log("REQ_GET_LINK_CAPABILITIES linkCapabilities" + lc);
722                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_LINK_CAPABILITIES, lc);
723                    break;
724                }
725                case DataConnectionAc.REQ_RESET:
726                    if (VDBG) log("DcDefaultState: msg.what=REQ_RESET");
727                    mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
728                    transitionTo(mInactiveState);
729                    break;
730                case DataConnectionAc.REQ_GET_REFCOUNT: {
731                    if (VDBG) log("REQ_GET_REFCOUNT  refCount=" + mRefCount);
732                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount);
733                    break;
734                }
735                case DataConnectionAc.REQ_ADD_APNCONTEXT: {
736                    ApnContext apnContext = (ApnContext) msg.obj;
737                    if (VDBG) log("REQ_ADD_APNCONTEXT apn=" + apnContext.getApnType());
738                    if (!mApnList.contains(apnContext)) {
739                        mApnList.add(apnContext);
740                    }
741                    mAc.replyToMessage(msg, DataConnectionAc.RSP_ADD_APNCONTEXT);
742                    break;
743                }
744                case DataConnectionAc.REQ_REMOVE_APNCONTEXT: {
745                    ApnContext apnContext = (ApnContext) msg.obj;
746                    if (VDBG) log("REQ_REMOVE_APNCONTEXT apn=" + apnContext.getApnType());
747                    mApnList.remove(apnContext);
748                    mAc.replyToMessage(msg, DataConnectionAc.RSP_REMOVE_APNCONTEXT);
749                    break;
750                }
751                case DataConnectionAc.REQ_GET_APNCONTEXT_LIST: {
752                    if (VDBG) log("REQ_GET_APNCONTEXT_LIST num in list=" + mApnList.size());
753                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNCONTEXT_LIST,
754                                       new ArrayList<ApnContext>(mApnList));
755                    break;
756                }
757                case DataConnectionAc.REQ_SET_RECONNECT_INTENT: {
758                    PendingIntent intent = (PendingIntent) msg.obj;
759                    if (VDBG) log("REQ_SET_RECONNECT_INTENT");
760                    mReconnectIntent = intent;
761                    mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_RECONNECT_INTENT);
762                    break;
763                }
764                case DataConnectionAc.REQ_GET_RECONNECT_INTENT: {
765                    if (VDBG) log("REQ_GET_RECONNECT_INTENT");
766                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_RECONNECT_INTENT,
767                                       mReconnectIntent);
768                    break;
769                }
770                case EVENT_CONNECT:
771                    if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
772                    ConnectionParams cp = (ConnectionParams) msg.obj;
773                    notifyConnectCompleted(cp, FailCause.UNKNOWN);
774                    break;
775
776                case EVENT_DISCONNECT:
777                    if (DBG) {
778                        log("DcDefaultState deferring msg.what=EVENT_DISCONNECT" + mRefCount);
779                    }
780                    deferMessage(msg);
781                    break;
782
783                case EVENT_DISCONNECT_ALL:
784                    if (DBG) {
785                        log("DcDefaultState deferring msg.what=EVENT_DISCONNECT_ALL" + mRefCount);
786                    }
787                    deferMessage(msg);
788                    break;
789
790                case EVENT_RIL_CONNECTED:
791                    ar = (AsyncResult)msg.obj;
792                    if (ar.exception == null) {
793                        mRilVersion = (Integer)ar.result;
794                        if (DBG) {
795                            log("DcDefaultState: msg.what=EVENT_RIL_CONNECTED mRilVersion=" +
796                                mRilVersion);
797                        }
798                    } else {
799                        log("Unexpected exception on EVENT_RIL_CONNECTED");
800                        mRilVersion = -1;
801                    }
802                    break;
803
804                default:
805                    if (DBG) {
806                        log("DcDefaultState: shouldn't happen but ignore msg.what=0x" +
807                                Integer.toHexString(msg.what));
808                    }
809                    break;
810            }
811
812            return retVal;
813        }
814    }
815    private DcDefaultState mDefaultState = new DcDefaultState();
816
817    /**
818     * The state machine is inactive and expects a EVENT_CONNECT.
819     */
820    private class DcInactiveState extends State {
821        private ConnectionParams mConnectionParams = null;
822        private FailCause mFailCause = null;
823        private DisconnectParams mDisconnectParams = null;
824
825        public void setEnterNotificationParams(ConnectionParams cp, FailCause cause,
826                                               int retryOverride) {
827            if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
828            mConnectionParams = cp;
829            mFailCause = cause;
830            mRetryOverride = retryOverride;
831        }
832
833        public void setEnterNotificationParams(DisconnectParams dp) {
834            if (VDBG) log("DcInactiveState: setEnterNoticationParams dp");
835            mDisconnectParams = dp;
836        }
837
838        @Override
839        public void enter() {
840            mTag += 1;
841
842            /**
843             * Now that we've transitioned to Inactive state we
844             * can send notifications. Previously we sent the
845             * notifications in the processMessage handler but
846             * that caused a race condition because the synchronous
847             * call to isInactive.
848             */
849            if ((mConnectionParams != null) && (mFailCause != null)) {
850                if (VDBG) log("DcInactiveState: enter notifyConnectCompleted");
851                notifyConnectCompleted(mConnectionParams, mFailCause);
852            }
853            if (mDisconnectParams != null) {
854                if (VDBG) log("DcInactiveState: enter notifyDisconnectCompleted");
855                notifyDisconnectCompleted(mDisconnectParams, true);
856            }
857            clearSettings();
858        }
859
860        @Override
861        public void exit() {
862            // clear notifications
863            mConnectionParams = null;
864            mFailCause = null;
865            mDisconnectParams = null;
866        }
867
868        @Override
869        public boolean processMessage(Message msg) {
870            boolean retVal;
871
872            switch (msg.what) {
873                case DataConnectionAc.REQ_RESET:
874                    if (DBG) {
875                        log("DcInactiveState: msg.what=RSP_RESET, ignore we're already reset");
876                    }
877                    mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
878                    retVal = HANDLED;
879                    break;
880
881                case EVENT_CONNECT:
882                    ConnectionParams cp = (ConnectionParams) msg.obj;
883                    cp.tag = mTag;
884                    if (DBG) {
885                        log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = "
886                                + mRefCount);
887                    }
888                    mRefCount = 1;
889                    onConnect(cp);
890                    transitionTo(mActivatingState);
891                    retVal = HANDLED;
892                    break;
893
894                case EVENT_DISCONNECT:
895                    if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT");
896                    notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
897                    retVal = HANDLED;
898                    break;
899
900                case EVENT_DISCONNECT_ALL:
901                    if (DBG) log("DcInactiveState: msg.what=EVENT_DISCONNECT_ALL");
902                    notifyDisconnectCompleted((DisconnectParams)msg.obj, false);
903                    retVal = HANDLED;
904                    break;
905
906                default:
907                    if (VDBG) {
908                        log("DcInactiveState nothandled msg.what=0x" +
909                                Integer.toHexString(msg.what));
910                    }
911                    retVal = NOT_HANDLED;
912                    break;
913            }
914            return retVal;
915        }
916    }
917    private DcInactiveState mInactiveState = new DcInactiveState();
918
919    /**
920     * The state machine is activating a connection.
921     */
922    private class DcActivatingState extends State {
923        @Override
924        public boolean processMessage(Message msg) {
925            boolean retVal;
926            AsyncResult ar;
927            ConnectionParams cp;
928
929            switch (msg.what) {
930                case EVENT_CONNECT:
931                    if (DBG) log("DcActivatingState deferring msg.what=EVENT_CONNECT refCount = "
932                            + mRefCount);
933                    deferMessage(msg);
934                    retVal = HANDLED;
935                    break;
936
937                case EVENT_SETUP_DATA_CONNECTION_DONE:
938                    if (DBG) log("DcActivatingState msg.what=EVENT_SETUP_DATA_CONNECTION_DONE");
939
940                    ar = (AsyncResult) msg.obj;
941                    cp = (ConnectionParams) ar.userObj;
942
943                    DataCallState.SetupResult result = onSetupConnectionCompleted(ar);
944                    if (DBG) log("DcActivatingState onSetupConnectionCompleted result=" + result);
945                    switch (result) {
946                        case SUCCESS:
947                            // All is well
948                            mActiveState.setEnterNotificationParams(cp, FailCause.NONE);
949                            transitionTo(mActiveState);
950                            break;
951                        case ERR_BadCommand:
952                            // Vendor ril rejected the command and didn't connect.
953                            // Transition to inactive but send notifications after
954                            // we've entered the mInactive state.
955                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause, -1);
956                            transitionTo(mInactiveState);
957                            break;
958                        case ERR_UnacceptableParameter:
959                            // The addresses given from the RIL are bad
960                            tearDownData(cp);
961                            transitionTo(mDisconnectingErrorCreatingConnection);
962                            break;
963                        case ERR_GetLastErrorFromRil:
964                            // Request failed and this is an old RIL
965                            phone.mCM.getLastDataCallFailCause(
966                                    obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp));
967                            break;
968                        case ERR_RilError:
969                            // Request failed and mFailCause has the reason
970                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
971                                                                      getSuggestedRetryTime(ar));
972                            transitionTo(mInactiveState);
973                            break;
974                        case ERR_Stale:
975                            // Request is stale, ignore.
976                            break;
977                        default:
978                            throw new RuntimeException("Unknown SetupResult, should not happen");
979                    }
980                    retVal = HANDLED;
981                    break;
982
983                case EVENT_GET_LAST_FAIL_DONE:
984                    ar = (AsyncResult) msg.obj;
985                    cp = (ConnectionParams) ar.userObj;
986                    FailCause cause = FailCause.UNKNOWN;
987
988                    if (cp.tag == mTag) {
989                        if (DBG) log("DcActivatingState msg.what=EVENT_GET_LAST_FAIL_DONE");
990                        if (ar.exception == null) {
991                            int rilFailCause = ((int[]) (ar.result))[0];
992                            cause = FailCause.fromInt(rilFailCause);
993                        }
994                        // Transition to inactive but send notifications after
995                        // we've entered the mInactive state.
996                        mInactiveState.setEnterNotificationParams(cp, cause, -1);
997                        transitionTo(mInactiveState);
998                    } else {
999                        if (DBG) {
1000                            log("DcActivatingState EVENT_GET_LAST_FAIL_DONE is stale cp.tag="
1001                                + cp.tag + ", mTag=" + mTag);
1002                        }
1003                    }
1004
1005                    retVal = HANDLED;
1006                    break;
1007
1008                default:
1009                    if (VDBG) {
1010                        log("DcActivatingState not handled msg.what=0x" +
1011                                Integer.toHexString(msg.what));
1012                    }
1013                    retVal = NOT_HANDLED;
1014                    break;
1015            }
1016            return retVal;
1017        }
1018    }
1019    private DcActivatingState mActivatingState = new DcActivatingState();
1020
1021    /**
1022     * The state machine is connected, expecting an EVENT_DISCONNECT.
1023     */
1024    private class DcActiveState extends State {
1025        private ConnectionParams mConnectionParams = null;
1026        private FailCause mFailCause = null;
1027
1028        public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
1029            if (VDBG) log("DcInactiveState: setEnterNoticationParams cp,cause");
1030            mConnectionParams = cp;
1031            mFailCause = cause;
1032        }
1033
1034        @Override public void enter() {
1035            /**
1036             * Now that we've transitioned to Active state we
1037             * can send notifications. Previously we sent the
1038             * notifications in the processMessage handler but
1039             * that caused a race condition because the synchronous
1040             * call to isActive.
1041             */
1042            if ((mConnectionParams != null) && (mFailCause != null)) {
1043                if (VDBG) log("DcActiveState: enter notifyConnectCompleted");
1044                notifyConnectCompleted(mConnectionParams, mFailCause);
1045            }
1046        }
1047
1048        @Override
1049        public void exit() {
1050            // clear notifications
1051            mConnectionParams = null;
1052            mFailCause = null;
1053        }
1054
1055        @Override
1056        public boolean processMessage(Message msg) {
1057            boolean retVal;
1058
1059            switch (msg.what) {
1060                case EVENT_CONNECT:
1061                    mRefCount++;
1062                    if (DBG) log("DcActiveState msg.what=EVENT_CONNECT RefCount=" + mRefCount);
1063                    if (msg.obj != null) {
1064                        notifyConnectCompleted((ConnectionParams) msg.obj, FailCause.NONE);
1065                    }
1066                    retVal = HANDLED;
1067                    break;
1068                case EVENT_DISCONNECT:
1069                    mRefCount--;
1070                    if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" + mRefCount);
1071                    if (mRefCount == 0)
1072                    {
1073                        DisconnectParams dp = (DisconnectParams) msg.obj;
1074                        dp.tag = mTag;
1075                        tearDownData(dp);
1076                        transitionTo(mDisconnectingState);
1077                    } else {
1078                        if (msg.obj != null) {
1079                            notifyDisconnectCompleted((DisconnectParams) msg.obj, false);
1080                        }
1081                    }
1082                    retVal = HANDLED;
1083                    break;
1084
1085                case EVENT_DISCONNECT_ALL:
1086                    if (DBG) {
1087                        log("DcActiveState msg.what=EVENT_DISCONNECT_ALL RefCount=" + mRefCount);
1088                    }
1089                    mRefCount = 0;
1090                    DisconnectParams dp = (DisconnectParams) msg.obj;
1091                    dp.tag = mTag;
1092                    tearDownData(dp);
1093                    transitionTo(mDisconnectingState);
1094                    retVal = HANDLED;
1095                    break;
1096
1097                default:
1098                    if (VDBG) {
1099                        log("DcActiveState not handled msg.what=0x" +
1100                                Integer.toHexString(msg.what));
1101                    }
1102                    retVal = NOT_HANDLED;
1103                    break;
1104            }
1105            return retVal;
1106        }
1107    }
1108    private DcActiveState mActiveState = new DcActiveState();
1109
1110    /**
1111     * The state machine is disconnecting.
1112     */
1113    private class DcDisconnectingState extends State {
1114        @Override
1115        public boolean processMessage(Message msg) {
1116            boolean retVal;
1117
1118            switch (msg.what) {
1119                case EVENT_CONNECT:
1120                    if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
1121                            + mRefCount);
1122                    deferMessage(msg);
1123                    retVal = HANDLED;
1124                    break;
1125
1126                case EVENT_DEACTIVATE_DONE:
1127                    if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE");
1128                    AsyncResult ar = (AsyncResult) msg.obj;
1129                    DisconnectParams dp = (DisconnectParams) ar.userObj;
1130                    if (dp.tag == mTag) {
1131                        // Transition to inactive but send notifications after
1132                        // we've entered the mInactive state.
1133                        mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
1134                        transitionTo(mInactiveState);
1135                    } else {
1136                        if (DBG) log("DcDisconnectState EVENT_DEACTIVATE_DONE stale dp.tag="
1137                                + dp.tag + " mTag=" + mTag);
1138                    }
1139                    retVal = HANDLED;
1140                    break;
1141
1142                default:
1143                    if (VDBG) {
1144                        log("DcDisconnectingState not handled msg.what=0x" +
1145                                Integer.toHexString(msg.what));
1146                    }
1147                    retVal = NOT_HANDLED;
1148                    break;
1149            }
1150            return retVal;
1151        }
1152    }
1153    private DcDisconnectingState mDisconnectingState = new DcDisconnectingState();
1154
1155    /**
1156     * The state machine is disconnecting after an creating a connection.
1157     */
1158    private class DcDisconnectionErrorCreatingConnection extends State {
1159        @Override
1160        public boolean processMessage(Message msg) {
1161            boolean retVal;
1162
1163            switch (msg.what) {
1164                case EVENT_DEACTIVATE_DONE:
1165                    AsyncResult ar = (AsyncResult) msg.obj;
1166                    ConnectionParams cp = (ConnectionParams) ar.userObj;
1167                    if (cp.tag == mTag) {
1168                        if (DBG) {
1169                            log("DcDisconnectionErrorCreatingConnection" +
1170                                " msg.what=EVENT_DEACTIVATE_DONE");
1171                        }
1172
1173                        // Transition to inactive but send notifications after
1174                        // we've entered the mInactive state.
1175                        mInactiveState.setEnterNotificationParams(cp,
1176                                FailCause.UNACCEPTABLE_NETWORK_PARAMETER, -1);
1177                        transitionTo(mInactiveState);
1178                    } else {
1179                        if (DBG) {
1180                            log("DcDisconnectionErrorCreatingConnection EVENT_DEACTIVATE_DONE" +
1181                                    " stale dp.tag=" + cp.tag + ", mTag=" + mTag);
1182                        }
1183                    }
1184                    retVal = HANDLED;
1185                    break;
1186
1187                default:
1188                    if (VDBG) {
1189                        log("DcDisconnectionErrorCreatingConnection not handled msg.what=0x"
1190                                + Integer.toHexString(msg.what));
1191                    }
1192                    retVal = NOT_HANDLED;
1193                    break;
1194            }
1195            return retVal;
1196        }
1197    }
1198    private DcDisconnectionErrorCreatingConnection mDisconnectingErrorCreatingConnection =
1199                new DcDisconnectionErrorCreatingConnection();
1200
1201    // ******* public interface
1202
1203    /**
1204     * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
1205     * Used for cellular networks that use Acesss Point Names (APN) such
1206     * as GSM networks.
1207     *
1208     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
1209     *        With AsyncResult.userObj set to the original msg.obj,
1210     *        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
1211     * @param apn is the Access Point Name to bring up a connection to
1212     */
1213    public void bringUp(Message onCompletedMsg, ApnSetting apn) {
1214        sendMessage(obtainMessage(EVENT_CONNECT, new ConnectionParams(apn, onCompletedMsg)));
1215    }
1216
1217    /**
1218     * Tear down the connection through the apn on the network.
1219     *
1220     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
1221     *        With AsyncResult.userObj set to the original msg.obj.
1222     */
1223    public void tearDown(String reason, Message onCompletedMsg) {
1224        sendMessage(obtainMessage(EVENT_DISCONNECT, new DisconnectParams(reason, onCompletedMsg)));
1225    }
1226
1227    /**
1228     * Tear down the connection through the apn on the network.  Ignores refcount and
1229     * and always tears down.
1230     *
1231     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
1232     *        With AsyncResult.userObj set to the original msg.obj.
1233     */
1234    public void tearDownAll(String reason, Message onCompletedMsg) {
1235        sendMessage(obtainMessage(EVENT_DISCONNECT_ALL,
1236                new DisconnectParams(reason, onCompletedMsg)));
1237    }
1238
1239    /**
1240     * @return the string for msg.what as our info.
1241     */
1242    @Override
1243    protected String getWhatToString(int what) {
1244        String info = null;
1245        info = cmdToString(what);
1246        if (info == null) {
1247            info = DataConnectionAc.cmdToString(what);
1248        }
1249        return info;
1250    }
1251
1252    /**
1253     * Dump the current state.
1254     *
1255     * @param fd
1256     * @param pw
1257     * @param args
1258     */
1259    @Override
1260    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1261        pw.print("DataConnection ");
1262        super.dump(fd, pw, args);
1263        pw.println(" mApnList=" + mApnList);
1264        pw.flush();
1265        pw.println(" mDataConnectionTracker=" + mDataConnectionTracker);
1266        pw.println(" mApn=" + mApn);
1267        pw.println(" mTag=" + mTag);
1268        pw.flush();
1269        pw.println(" phone=" + phone);
1270        pw.println(" mRilVersion=" + mRilVersion);
1271        pw.println(" cid=" + cid);
1272        pw.flush();
1273        pw.println(" mLinkProperties=" + mLinkProperties);
1274        pw.flush();
1275        pw.println(" mCapabilities=" + mCapabilities);
1276        pw.println(" createTime=" + TimeUtils.logTimeOfDay(createTime));
1277        pw.println(" lastFailTime=" + TimeUtils.logTimeOfDay(lastFailTime));
1278        pw.println(" lastFailCause=" + lastFailCause);
1279        pw.flush();
1280        pw.println(" mRetryOverride=" + mRetryOverride);
1281        pw.println(" mRefCount=" + mRefCount);
1282        pw.println(" userData=" + userData);
1283        if (mRetryMgr != null) pw.println(" " + mRetryMgr);
1284        pw.flush();
1285    }
1286}
1287