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 
17 package com.android.internal.telephony.dataconnection;
18 
19 import android.app.PendingIntent;
20 import android.net.ConnectivityManager;
21 import android.net.NetworkCapabilities;
22 import android.net.NetworkConfig;
23 import android.net.NetworkRequest;
24 import android.os.Message;
25 import android.telephony.Rlog;
26 import android.telephony.data.ApnSetting;
27 import android.telephony.data.ApnSetting.ApnType;
28 import android.text.TextUtils;
29 import android.util.LocalLog;
30 import android.util.SparseIntArray;
31 
32 import com.android.internal.R;
33 import com.android.internal.telephony.DctConstants;
34 import com.android.internal.telephony.Phone;
35 import com.android.internal.telephony.RetryManager;
36 import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
37 import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
38 import com.android.internal.util.IndentingPrintWriter;
39 
40 import java.io.FileDescriptor;
41 import java.io.PrintWriter;
42 import java.util.ArrayList;
43 import java.util.List;
44 import java.util.concurrent.atomic.AtomicBoolean;
45 import java.util.concurrent.atomic.AtomicInteger;
46 
47 /**
48  * Maintain the Apn context
49  */
50 public class ApnContext {
51 
52     public final String LOG_TAG;
53     private final static String SLOG_TAG = "ApnContext";
54 
55     protected static final boolean DBG = false;
56 
57     private final Phone mPhone;
58 
59     private final String mApnType;
60 
61     private DctConstants.State mState;
62 
63     public final int priority;
64 
65     private ApnSetting mApnSetting;
66 
67     private DataConnection mDataConnection;
68 
69     String mReason;
70 
71     PendingIntent mReconnectAlarmIntent;
72 
73     /**
74      * user/app requested connection on this APN
75      */
76     AtomicBoolean mDataEnabled;
77 
78     private final Object mRefCountLock = new Object();
79     private int mRefCount = 0;
80 
81     /**
82      * carrier requirements met
83      */
84     AtomicBoolean mDependencyMet;
85 
86     private final DcTracker mDcTracker;
87 
88     /**
89      * Remember this as a change in this value to a more permissive state
90      * should cause us to retry even permanent failures
91      */
92     private boolean mConcurrentVoiceAndDataAllowed;
93 
94     /**
95      * used to track a single connection request so disconnects can get ignored if
96      * obsolete.
97      */
98     private final AtomicInteger mConnectionGeneration = new AtomicInteger(0);
99 
100     /**
101      * Retry manager that handles the APN retry and delays.
102      */
103     private final RetryManager mRetryManager;
104 
105     /**
106      * AonContext constructor
107      * @param phone phone object
108      * @param apnType APN type (e.g. default, supl, mms, etc...)
109      * @param logTag Tag for logging
110      * @param config Network configuration
111      * @param tracker Data call tracker
112      */
ApnContext(Phone phone, String apnType, String logTag, NetworkConfig config, DcTracker tracker)113     public ApnContext(Phone phone, String apnType, String logTag, NetworkConfig config,
114             DcTracker tracker) {
115         mPhone = phone;
116         mApnType = apnType;
117         mState = DctConstants.State.IDLE;
118         setReason(Phone.REASON_DATA_ENABLED);
119         mDataEnabled = new AtomicBoolean(false);
120         mDependencyMet = new AtomicBoolean(config.dependencyMet);
121         priority = config.priority;
122         LOG_TAG = logTag;
123         mDcTracker = tracker;
124         mRetryManager = new RetryManager(phone, apnType);
125     }
126 
127     /**
128      * Get the APN type
129      * @return The APN type
130      */
getApnType()131     public String getApnType() {
132         return mApnType;
133     }
134 
135     /**
136      * Gets the APN type bitmask.
137      * @return The APN type bitmask
138      */
getApnTypeBitmask()139     public int getApnTypeBitmask() {
140         return ApnSetting.getApnTypesBitmaskFromString(mApnType);
141     }
142 
143     /**
144      * Get the associated data connection
145      * @return The data connection
146      */
getDataConnection()147     public synchronized DataConnection getDataConnection() {
148         return mDataConnection;
149     }
150 
151     /**
152      * Set the associated data connection.
153      * @param dc data connection
154      */
setDataConnection(DataConnection dc)155     public synchronized void setDataConnection(DataConnection dc) {
156         log("setDataConnectionAc: old=" + mDataConnection + ",new=" + dc + " this=" + this);
157         mDataConnection = dc;
158     }
159 
160     /**
161      * Release data connection.
162      * @param reason The reason of releasing data connection
163      */
releaseDataConnection(String reason)164     public synchronized void releaseDataConnection(String reason) {
165         if (mDataConnection != null) {
166             mDataConnection.tearDown(this, reason, null);
167             mDataConnection = null;
168         }
169         setState(DctConstants.State.IDLE);
170     }
171 
172     /**
173      * Get the reconnect intent.
174      * @return The reconnect intent
175      */
getReconnectIntent()176     public synchronized PendingIntent getReconnectIntent() {
177         return mReconnectAlarmIntent;
178     }
179 
180     /**
181      * Save the reconnect intent which can be used for cancelling later.
182      * @param intent The reconnect intent
183      */
setReconnectIntent(PendingIntent intent)184     public synchronized void setReconnectIntent(PendingIntent intent) {
185         mReconnectAlarmIntent = intent;
186     }
187 
188     /**
189      * Get the current APN setting.
190      * @return APN setting
191      */
getApnSetting()192     public synchronized ApnSetting getApnSetting() {
193         log("getApnSetting: apnSetting=" + mApnSetting);
194         return mApnSetting;
195     }
196 
197     /**
198      * Set the APN setting.
199      * @param apnSetting APN setting
200      */
setApnSetting(ApnSetting apnSetting)201     public synchronized void setApnSetting(ApnSetting apnSetting) {
202         log("setApnSetting: apnSetting=" + apnSetting);
203         mApnSetting = apnSetting;
204     }
205 
206     /**
207      * Set the list of APN candidates which will be used for data call setup later.
208      * @param waitingApns List of APN candidates
209      */
setWaitingApns(ArrayList<ApnSetting> waitingApns)210     public synchronized void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
211         mRetryManager.setWaitingApns(waitingApns);
212     }
213 
214     /**
215      * Get the next available APN to try.
216      * @return APN setting which will be used for data call setup. Return null if there is no
217      * APN can be retried.
218      */
getNextApnSetting()219     public ApnSetting getNextApnSetting() {
220         return mRetryManager.getNextApnSetting();
221     }
222 
223     /**
224      * Save the modem suggested delay for retrying the current APN.
225      * This method is called when we get the suggested delay from RIL.
226      * @param delay The delay in milliseconds
227      */
setModemSuggestedDelay(long delay)228     public void setModemSuggestedDelay(long delay) {
229         mRetryManager.setModemSuggestedDelay(delay);
230     }
231 
232     /**
233      * Get the delay for trying the next APN setting if the current one failed.
234      * @param failFastEnabled True if fail fast mode enabled. In this case we'll use a shorter
235      *                        delay.
236      * @return The delay in milliseconds
237      */
getDelayForNextApn(boolean failFastEnabled)238     public long getDelayForNextApn(boolean failFastEnabled) {
239         return mRetryManager.getDelayForNextApn(failFastEnabled || isFastRetryReason());
240     }
241 
242     /**
243      * Mark the current APN setting permanently failed, which means it will not be retried anymore.
244      * @param apn APN setting
245      */
markApnPermanentFailed(ApnSetting apn)246     public void markApnPermanentFailed(ApnSetting apn) {
247         mRetryManager.markApnPermanentFailed(apn);
248     }
249 
250     /**
251      * Get the list of waiting APNs.
252      * @return the list of waiting APNs
253      */
getWaitingApns()254     public ArrayList<ApnSetting> getWaitingApns() {
255         return mRetryManager.getWaitingApns();
256     }
257 
258     /**
259      * Save the state indicating concurrent voice/data allowed.
260      * @param allowed True if concurrent voice/data is allowed
261      */
setConcurrentVoiceAndDataAllowed(boolean allowed)262     public synchronized void setConcurrentVoiceAndDataAllowed(boolean allowed) {
263         mConcurrentVoiceAndDataAllowed = allowed;
264     }
265 
266     /**
267      * Get the state indicating concurrent voice/data allowed.
268      * @return True if concurrent voice/data is allowed
269      */
isConcurrentVoiceAndDataAllowed()270     public synchronized boolean isConcurrentVoiceAndDataAllowed() {
271         return mConcurrentVoiceAndDataAllowed;
272     }
273 
274     /**
275      * Set the current data call state.
276      * @param s Current data call state
277      */
setState(DctConstants.State s)278     public synchronized void setState(DctConstants.State s) {
279         log("setState: " + s + ", previous state:" + mState);
280 
281         if (mState != s) {
282             mStateLocalLog.log("State changed from " + mState + " to " + s);
283             mState = s;
284         }
285 
286         if (mState == DctConstants.State.FAILED) {
287             if (mRetryManager.getWaitingApns() != null) {
288                 // when teardown the connection and set to IDLE
289                 mRetryManager.getWaitingApns().clear();
290             }
291         }
292     }
293 
294     /**
295      * Get the current data call state.
296      * @return The current data call state
297      */
getState()298     public synchronized DctConstants.State getState() {
299         return mState;
300     }
301 
302     /**
303      * Check whether the data call is disconnected or not.
304      * @return True if the data call is disconnected
305      */
isDisconnected()306     public boolean isDisconnected() {
307         DctConstants.State currentState = getState();
308         return ((currentState == DctConstants.State.IDLE) ||
309                     currentState == DctConstants.State.FAILED);
310     }
311 
312     /**
313      * Set the reason for data call connection.
314      * @param reason Reason for data call connection
315      */
setReason(String reason)316     public synchronized void setReason(String reason) {
317         log("set reason as " + reason + ",current state " + mState);
318         mReason = reason;
319     }
320 
321     /**
322      * Get the reason for data call connection.
323      * @return The reason for data call connection
324      */
getReason()325     public synchronized String getReason() {
326         return mReason;
327     }
328 
329     /**
330      * Check if ready for data call connection
331      * @return True if ready, otherwise false.
332      */
isReady()333     public boolean isReady() {
334         return mDataEnabled.get() && mDependencyMet.get();
335     }
336 
337     /**
338      * Check if the data call is in the state which allow connecting.
339      * @return True if allowed, otherwise false.
340      */
isConnectable()341     public boolean isConnectable() {
342         return isReady() && ((mState == DctConstants.State.IDLE)
343                                 || (mState == DctConstants.State.RETRYING)
344                                 || (mState == DctConstants.State.FAILED));
345     }
346 
347     /**
348      * Check if apn reason is fast retry reason which should apply shorter delay between apn re-try.
349      * @return True if it is fast retry reason, otherwise false.
350      */
isFastRetryReason()351     private boolean isFastRetryReason() {
352         return Phone.REASON_NW_TYPE_CHANGED.equals(mReason) ||
353                 Phone.REASON_APN_CHANGED.equals(mReason);
354     }
355 
356     /** Check if the data call is in connected or connecting state.
357      * @return True if the data call is in connected or connecting state
358      */
isConnectedOrConnecting()359     public boolean isConnectedOrConnecting() {
360         return isReady() && ((mState == DctConstants.State.CONNECTED)
361                                 || (mState == DctConstants.State.CONNECTING)
362                                 || (mState == DctConstants.State.RETRYING));
363     }
364 
365     /**
366      * Set data call enabled/disabled state.
367      * @param enabled True if data call is enabled
368      */
setEnabled(boolean enabled)369     public void setEnabled(boolean enabled) {
370         log("set enabled as " + enabled + ", current state is " + mDataEnabled.get());
371         mDataEnabled.set(enabled);
372     }
373 
374     /**
375      * Check if the data call is enabled or not.
376      * @return True if enabled
377      */
isEnabled()378     public boolean isEnabled() {
379         return mDataEnabled.get();
380     }
381 
isDependencyMet()382     public boolean isDependencyMet() {
383        return mDependencyMet.get();
384     }
385 
isProvisioningApn()386     public boolean isProvisioningApn() {
387         String provisioningApn = mPhone.getContext().getResources()
388                 .getString(R.string.mobile_provisioning_apn);
389         if (!TextUtils.isEmpty(provisioningApn) &&
390                 (mApnSetting != null) && (mApnSetting.getApnName() != null)) {
391             return (mApnSetting.getApnName().equals(provisioningApn));
392         } else {
393             return false;
394         }
395     }
396 
397     private final LocalLog mLocalLog = new LocalLog(150);
398     private final ArrayList<NetworkRequest> mNetworkRequests = new ArrayList<>();
399     private final LocalLog mStateLocalLog = new LocalLog(50);
400 
requestLog(String str)401     public void requestLog(String str) {
402         synchronized (mLocalLog) {
403             mLocalLog.log(str);
404         }
405     }
406 
requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type, Message onCompleteMsg)407     public void requestNetwork(NetworkRequest networkRequest, @RequestNetworkType int type,
408                                Message onCompleteMsg) {
409         synchronized (mRefCountLock) {
410             mNetworkRequests.add(networkRequest);
411             logl("requestNetwork for " + networkRequest + ", type="
412                     + DcTracker.requestTypeToString(type));
413             mDcTracker.enableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type,
414                     onCompleteMsg);
415             if (mDataConnection != null) {
416                 // New network request added. Should re-evaluate properties of
417                 // the data connection. For example, the score may change.
418                 mDataConnection.reevaluateDataConnectionProperties();
419             }
420         }
421     }
422 
releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type)423     public void releaseNetwork(NetworkRequest networkRequest, @ReleaseNetworkType int type) {
424         synchronized (mRefCountLock) {
425             if (mNetworkRequests.contains(networkRequest) == false) {
426                 logl("releaseNetwork can't find this request (" + networkRequest + ")");
427             } else {
428                 mNetworkRequests.remove(networkRequest);
429                 if (mDataConnection != null) {
430                     // New network request added. Should re-evaluate properties of
431                     // the data connection. For example, the score may change.
432                     mDataConnection.reevaluateDataConnectionProperties();
433                 }
434                 logl("releaseNetwork left with " + mNetworkRequests.size()
435                         + " requests.");
436                 if (mNetworkRequests.size() == 0
437                         || type == DcTracker.RELEASE_TYPE_DETACH
438                         || type == DcTracker.RELEASE_TYPE_HANDOVER) {
439                     mDcTracker.disableApn(ApnSetting.getApnTypesBitmaskFromString(mApnType), type);
440                 }
441             }
442         }
443     }
444 
445     /**
446      * @param excludeDun True if excluding requests that have DUN capability
447      * @return True if the attached network requests contain restricted capability.
448      */
hasRestrictedRequests(boolean excludeDun)449     public boolean hasRestrictedRequests(boolean excludeDun) {
450         synchronized (mRefCountLock) {
451             for (NetworkRequest nr : mNetworkRequests) {
452                 if (excludeDun &&
453                         nr.networkCapabilities.hasCapability(
454                         NetworkCapabilities.NET_CAPABILITY_DUN)) {
455                     continue;
456                 }
457                 if (!nr.networkCapabilities.hasCapability(
458                         NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) {
459                     return true;
460                 }
461             }
462         }
463         return false;
464     }
465 
466     private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray();
467 
resetErrorCodeRetries()468     public void resetErrorCodeRetries() {
469         logl("ApnContext.resetErrorCodeRetries");
470 
471         String[] config = mPhone.getContext().getResources().getStringArray(
472                 com.android.internal.R.array.config_cell_retries_per_error_code);
473         synchronized (mRetriesLeftPerErrorCode) {
474             mRetriesLeftPerErrorCode.clear();
475 
476             for (String c : config) {
477                 String errorValue[] = c.split(",");
478                 if (errorValue != null && errorValue.length == 2) {
479                     int count = 0;
480                     int errorCode = 0;
481                     try {
482                         errorCode = Integer.parseInt(errorValue[0]);
483                         count = Integer.parseInt(errorValue[1]);
484                     } catch (NumberFormatException e) {
485                         log("Exception parsing config_retries_per_error_code: " + e);
486                         continue;
487                     }
488                     if (count > 0 && errorCode > 0) {
489                         mRetriesLeftPerErrorCode.put(errorCode, count);
490                     }
491                 } else {
492                     log("Exception parsing config_retries_per_error_code: " + c);
493                 }
494             }
495         }
496     }
497 
restartOnError(int errorCode)498     public boolean restartOnError(int errorCode) {
499         boolean result = false;
500         int retriesLeft = 0;
501         synchronized(mRetriesLeftPerErrorCode) {
502             retriesLeft = mRetriesLeftPerErrorCode.get(errorCode);
503             switch (retriesLeft) {
504                 case 0: {
505                     // not set, never restart modem
506                     break;
507                 }
508                 case 1: {
509                     resetErrorCodeRetries();
510                     result = true;
511                     break;
512                 }
513                 default: {
514                     mRetriesLeftPerErrorCode.put(errorCode, retriesLeft - 1);
515                     result = false;
516                 }
517             }
518         }
519         logl("ApnContext.restartOnError(" + errorCode + ") found " + retriesLeft
520                 + " and returned " + result);
521         return result;
522     }
523 
incAndGetConnectionGeneration()524     public int incAndGetConnectionGeneration() {
525         return mConnectionGeneration.incrementAndGet();
526     }
527 
getConnectionGeneration()528     public int getConnectionGeneration() {
529         return mConnectionGeneration.get();
530     }
531 
getRetryAfterDisconnectDelay()532     long getRetryAfterDisconnectDelay() {
533         return mRetryManager.getRetryAfterDisconnectDelay();
534     }
535 
getApnTypeFromNetworkType(int networkType)536     public static int getApnTypeFromNetworkType(int networkType) {
537         switch (networkType) {
538             case ConnectivityManager.TYPE_MOBILE:
539                 return ApnSetting.TYPE_DEFAULT;
540             case ConnectivityManager.TYPE_MOBILE_MMS:
541                 return ApnSetting.TYPE_MMS;
542             case ConnectivityManager.TYPE_MOBILE_SUPL:
543                 return ApnSetting.TYPE_SUPL;
544             case ConnectivityManager.TYPE_MOBILE_DUN:
545                 return ApnSetting.TYPE_DUN;
546             case ConnectivityManager.TYPE_MOBILE_FOTA:
547                 return ApnSetting.TYPE_FOTA;
548             case ConnectivityManager.TYPE_MOBILE_IMS:
549                 return ApnSetting.TYPE_IMS;
550             case ConnectivityManager.TYPE_MOBILE_CBS:
551                 return ApnSetting.TYPE_CBS;
552             case ConnectivityManager.TYPE_MOBILE_IA:
553                 return ApnSetting.TYPE_IA;
554             case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
555                 return ApnSetting.TYPE_EMERGENCY;
556             default:
557                 return ApnSetting.TYPE_NONE;
558         }
559     }
560 
getApnTypeFromNetworkRequest(NetworkRequest nr)561     static @ApnType int getApnTypeFromNetworkRequest(NetworkRequest nr) {
562         NetworkCapabilities nc = nr.networkCapabilities;
563         // For now, ignore the bandwidth stuff
564         if (nc.getTransportTypes().length > 0 &&
565                 nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) {
566             return ApnSetting.TYPE_NONE;
567         }
568 
569         // in the near term just do 1-1 matches.
570         // TODO - actually try to match the set of capabilities
571         int apnType = ApnSetting.TYPE_NONE;
572         boolean error = false;
573 
574         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
575             apnType = ApnSetting.TYPE_DEFAULT;
576         }
577         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
578             if (apnType != ApnSetting.TYPE_NONE) error = true;
579             apnType = ApnSetting.TYPE_MMS;
580         }
581         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
582             if (apnType != ApnSetting.TYPE_NONE) error = true;
583             apnType = ApnSetting.TYPE_SUPL;
584         }
585         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
586             if (apnType != ApnSetting.TYPE_NONE) error = true;
587             apnType = ApnSetting.TYPE_DUN;
588         }
589         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
590             if (apnType != ApnSetting.TYPE_NONE) error = true;
591             apnType = ApnSetting.TYPE_FOTA;
592         }
593         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
594             if (apnType != ApnSetting.TYPE_NONE) error = true;
595             apnType = ApnSetting.TYPE_IMS;
596         }
597         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
598             if (apnType != ApnSetting.TYPE_NONE) error = true;
599             apnType = ApnSetting.TYPE_CBS;
600         }
601         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_IA)) {
602             if (apnType != ApnSetting.TYPE_NONE) error = true;
603             apnType = ApnSetting.TYPE_IA;
604         }
605         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
606             if (apnType != ApnSetting.TYPE_NONE) error = true;
607             apnType = ApnSetting.TYPE_EMERGENCY;
608         }
609         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MCX)) {
610             if (apnType != ApnSetting.TYPE_NONE) error = true;
611             apnType = ApnSetting.TYPE_MCX;
612         }
613         if (error) {
614             // TODO: If this error condition is removed, the framework's handling of
615             // NET_CAPABILITY_NOT_RESTRICTED will need to be updated so requests for
616             // say FOTA and INTERNET are marked as restricted.  This is not how
617             // NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
618             Rlog.d(SLOG_TAG, "Multiple apn types specified in request - result is unspecified!");
619         }
620         if (apnType == ApnSetting.TYPE_NONE) {
621             Rlog.d(SLOG_TAG, "Unsupported NetworkRequest in Telephony: nr=" + nr);
622         }
623         return apnType;
624     }
625 
getNetworkRequests()626     public List<NetworkRequest> getNetworkRequests() {
627         synchronized (mRefCountLock) {
628             return new ArrayList<NetworkRequest>(mNetworkRequests);
629         }
630     }
631 
632     @Override
toString()633     public synchronized String toString() {
634         // We don't print mDataConnection because its recursive.
635         return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={" +
636                 mRetryManager.getWaitingApns() + "}" + " mApnSetting={" + mApnSetting +
637                 "} mReason=" + mReason + " mDataEnabled=" + mDataEnabled + " mDependencyMet=" +
638                 mDependencyMet + "}";
639     }
640 
log(String s)641     private void log(String s) {
642         if (DBG) {
643             Rlog.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s);
644         }
645     }
646 
logl(String s)647     private void logl(String s) {
648         log(s);
649         mLocalLog.log(s);
650     }
651 
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)652     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
653         final IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
654         synchronized (mRefCountLock) {
655             pw.println(toString());
656             if (mNetworkRequests.size() > 0) {
657                 pw.println("NetworkRequests:");
658                 pw.increaseIndent();
659                 for (NetworkRequest nr : mNetworkRequests) {
660                     pw.println(nr);
661                 }
662                 pw.decreaseIndent();
663             }
664             pw.increaseIndent();
665             pw.println("-----");
666             pw.println("Local log:");
667             mLocalLog.dump(fd, pw, args);
668             pw.println("-----");
669             pw.decreaseIndent();
670             pw.println("Historical APN state:");
671             pw.increaseIndent();
672             mStateLocalLog.dump(fd, pw, args);
673             pw.decreaseIndent();
674             pw.println(mRetryManager);
675             pw.println("--------------------------");
676         }
677     }
678 }
679