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