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