• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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