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