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