ApnContext.java revision 1a87ab3d7170d618f048c4f5af8c7504a587aaa5
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 17package com.android.internal.telephony.dataconnection; 18 19import android.app.PendingIntent; 20import android.content.Context; 21import android.content.res.Resources; 22import android.net.NetworkConfig; 23import android.telephony.Rlog; 24import android.text.TextUtils; 25import android.util.LocalLog; 26import android.util.SparseIntArray; 27 28import com.android.internal.R; 29import com.android.internal.telephony.DctConstants; 30import com.android.internal.telephony.Phone; 31import com.android.internal.util.IndentingPrintWriter; 32 33import java.io.FileDescriptor; 34import java.io.PrintWriter; 35import java.util.ArrayList; 36import java.util.concurrent.atomic.AtomicBoolean; 37import java.util.concurrent.atomic.AtomicInteger; 38 39/** 40 * Maintain the Apn context 41 */ 42public class ApnContext { 43 44 public final String LOG_TAG; 45 46 protected static final boolean DBG = false; 47 48 private final Context mContext; 49 50 private final String mApnType; 51 52 private DctConstants.State mState; 53 54 private ArrayList<ApnSetting> mWaitingApns = null; 55 56 /** 57 * Used to check if conditions (new RAT) are resulting in a new list which warrants a retry. 58 * Set in the last trySetupData call. 59 */ 60 private ArrayList<ApnSetting> mOriginalWaitingApns = null; 61 62 public final int priority; 63 64 /** A zero indicates that all waiting APNs had a permanent error */ 65 private AtomicInteger mWaitingApnsPermanentFailureCountDown; 66 67 private ApnSetting mApnSetting; 68 69 DcAsyncChannel mDcAc; 70 71 String mReason; 72 73 PendingIntent mReconnectAlarmIntent; 74 75 /** 76 * user/app requested connection on this APN 77 */ 78 AtomicBoolean mDataEnabled; 79 80 private final Object mRefCountLock = new Object(); 81 private int mRefCount = 0; 82 83 /** 84 * carrier requirements met 85 */ 86 AtomicBoolean mDependencyMet; 87 88 private final DcTracker mDcTracker; 89 90 /** 91 * Remember this as a change in this value to a more permissive state 92 * should cause us to retry even permanent failures 93 */ 94 private boolean mConcurrentVoiceAndDataAllowed; 95 96 /** 97 * used to track a single connection request so disconnects can get ignored if 98 * obsolete. 99 */ 100 private final AtomicInteger mConnectionGeneration = new AtomicInteger(0); 101 102 public ApnContext(Context context, String apnType, String logTag, NetworkConfig config, 103 DcTracker tracker) { 104 mContext = context; 105 mApnType = apnType; 106 mState = DctConstants.State.IDLE; 107 setReason(Phone.REASON_DATA_ENABLED); 108 mDataEnabled = new AtomicBoolean(false); 109 mDependencyMet = new AtomicBoolean(config.dependencyMet); 110 mWaitingApnsPermanentFailureCountDown = new AtomicInteger(0); 111 priority = config.priority; 112 LOG_TAG = logTag; 113 mDcTracker = tracker; 114 } 115 116 public String getApnType() { 117 return mApnType; 118 } 119 120 public synchronized DcAsyncChannel getDcAc() { 121 return mDcAc; 122 } 123 124 public synchronized void setDataConnectionAc(DcAsyncChannel dcac) { 125 if (DBG) { 126 log("setDataConnectionAc: old dcac=" + mDcAc + " new dcac=" + dcac 127 + " this=" + this); 128 } 129 mDcAc = dcac; 130 } 131 132 public synchronized void releaseDataConnection(String reason) { 133 if (mDcAc != null) { 134 mDcAc.tearDown(this, reason, null); 135 mDcAc = null; 136 } 137 setState(DctConstants.State.IDLE); 138 } 139 140 public synchronized PendingIntent getReconnectIntent() { 141 return mReconnectAlarmIntent; 142 } 143 144 public synchronized void setReconnectIntent(PendingIntent intent) { 145 mReconnectAlarmIntent = intent; 146 } 147 148 public synchronized ApnSetting getApnSetting() { 149 if (DBG) log("getApnSetting: apnSetting=" + mApnSetting); 150 return mApnSetting; 151 } 152 153 public synchronized void setApnSetting(ApnSetting apnSetting) { 154 if (DBG) log("setApnSetting: apnSetting=" + apnSetting); 155 mApnSetting = apnSetting; 156 } 157 158 public synchronized void setWaitingApns(ArrayList<ApnSetting> waitingApns) { 159 mWaitingApns = waitingApns; 160 mOriginalWaitingApns = new ArrayList<ApnSetting>(waitingApns); 161 mWaitingApnsPermanentFailureCountDown.set(mWaitingApns.size()); 162 } 163 164 public int getWaitingApnsPermFailCount() { 165 return mWaitingApnsPermanentFailureCountDown.get(); 166 } 167 168 public void decWaitingApnsPermFailCount() { 169 mWaitingApnsPermanentFailureCountDown.decrementAndGet(); 170 } 171 172 public synchronized ApnSetting getNextWaitingApn() { 173 ArrayList<ApnSetting> list = mWaitingApns; 174 ApnSetting apn = null; 175 176 if (list != null) { 177 if (!list.isEmpty()) { 178 apn = list.get(0); 179 } 180 } 181 return apn; 182 } 183 184 public synchronized void removeWaitingApn(ApnSetting apn) { 185 if (mWaitingApns != null) { 186 mWaitingApns.remove(apn); 187 } 188 } 189 190 public synchronized ArrayList<ApnSetting> getOriginalWaitingApns() { 191 return mOriginalWaitingApns; 192 } 193 194 public synchronized ArrayList<ApnSetting> getWaitingApns() { 195 return mWaitingApns; 196 } 197 198 public synchronized void setConcurrentVoiceAndDataAllowed(boolean allowed) { 199 mConcurrentVoiceAndDataAllowed = allowed; 200 } 201 202 public synchronized boolean isConcurrentVoiceAndDataAllowed() { 203 return mConcurrentVoiceAndDataAllowed; 204 } 205 206 public synchronized void setState(DctConstants.State s) { 207 if (DBG) { 208 log("setState: " + s + ", previous state:" + mState); 209 } 210 211 mState = s; 212 213 if (mState == DctConstants.State.FAILED) { 214 if (mWaitingApns != null) { 215 mWaitingApns.clear(); // when teardown the connection and set to IDLE 216 } 217 } 218 } 219 220 public synchronized DctConstants.State getState() { 221 return mState; 222 } 223 224 public boolean isDisconnected() { 225 DctConstants.State currentState = getState(); 226 return ((currentState == DctConstants.State.IDLE) || 227 currentState == DctConstants.State.FAILED); 228 } 229 230 public synchronized void setReason(String reason) { 231 if (DBG) { 232 log("set reason as " + reason + ",current state " + mState); 233 } 234 mReason = reason; 235 } 236 237 public synchronized String getReason() { 238 return mReason; 239 } 240 241 public boolean isReady() { 242 return mDataEnabled.get() && mDependencyMet.get(); 243 } 244 245 public boolean isConnectable() { 246 return isReady() && ((mState == DctConstants.State.IDLE) 247 || (mState == DctConstants.State.SCANNING) 248 || (mState == DctConstants.State.RETRYING) 249 || (mState == DctConstants.State.FAILED)); 250 } 251 252 public boolean isConnectedOrConnecting() { 253 return isReady() && ((mState == DctConstants.State.CONNECTED) 254 || (mState == DctConstants.State.CONNECTING) 255 || (mState == DctConstants.State.SCANNING) 256 || (mState == DctConstants.State.RETRYING)); 257 } 258 259 public void setEnabled(boolean enabled) { 260 if (DBG) { 261 log("set enabled as " + enabled + ", current state is " + mDataEnabled.get()); 262 } 263 mDataEnabled.set(enabled); 264 } 265 266 public boolean isEnabled() { 267 return mDataEnabled.get(); 268 } 269 270 public void setDependencyMet(boolean met) { 271 if (DBG) { 272 log("set mDependencyMet as " + met + " current state is " + mDependencyMet.get()); 273 } 274 mDependencyMet.set(met); 275 } 276 277 public boolean getDependencyMet() { 278 return mDependencyMet.get(); 279 } 280 281 public boolean isProvisioningApn() { 282 String provisioningApn = mContext.getResources() 283 .getString(R.string.mobile_provisioning_apn); 284 if (!TextUtils.isEmpty(provisioningApn) && 285 (mApnSetting != null) && (mApnSetting.apn != null)) { 286 return (mApnSetting.apn.equals(provisioningApn)); 287 } else { 288 return false; 289 } 290 } 291 292 private final ArrayList<LocalLog> mLocalLogs = new ArrayList<LocalLog>(); 293 294 public void requestLog(String str) { 295 synchronized (mRefCountLock) { 296 for (LocalLog l : mLocalLogs) { 297 l.log(str); 298 } 299 } 300 } 301 302 public void incRefCount(LocalLog log) { 303 synchronized (mRefCountLock) { 304 if (mRefCount == 0) { 305 // we wanted to leave the last in so it could actually capture the tear down 306 // of the network 307 requestLog("clearing log with size=" + mLocalLogs.size()); 308 mLocalLogs.clear(); 309 } 310 if (mLocalLogs.contains(log)) { 311 log.log("ApnContext.incRefCount has duplicate add - " + mRefCount); 312 } else { 313 mLocalLogs.add(log); 314 log.log("ApnContext.incRefCount - " + mRefCount); 315 } 316 if (mRefCount++ == 0) { 317 mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), true); 318 } 319 } 320 } 321 322 public void decRefCount(LocalLog log) { 323 synchronized (mRefCountLock) { 324 // leave the last log alive to capture the actual tear down 325 if (mRefCount != 1) { 326 if (mLocalLogs.remove(log)) { 327 log.log("ApnContext.decRefCount - " + mRefCount); 328 } else { 329 log.log("ApnContext.decRefCount didn't find log - " + mRefCount); 330 } 331 } else { 332 log.log("ApnContext.decRefCount - 1"); 333 } 334 if (mRefCount-- == 1) { 335 mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), false); 336 } 337 if (mRefCount < 0) { 338 log.log("ApnContext.decRefCount went to " + mRefCount); 339 mRefCount = 0; 340 } 341 } 342 } 343 344 private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray(); 345 346 public void resetErrorCodeRetries() { 347 requestLog("ApnContext.resetErrorCodeRetries"); 348 if (DBG) log("ApnContext.resetErrorCodeRetries"); 349 350 String[] config = Resources.getSystem().getStringArray( 351 com.android.internal.R.array.config_cell_retries_per_error_code); 352 synchronized (mRetriesLeftPerErrorCode) { 353 mRetriesLeftPerErrorCode.clear(); 354 355 for (String c : config) { 356 String errorValue[] = c.split(","); 357 if (errorValue != null && errorValue.length == 2) { 358 int count = 0; 359 int errorCode = 0; 360 try { 361 errorCode = Integer.parseInt(errorValue[0]); 362 count = Integer.parseInt(errorValue[1]); 363 } catch (NumberFormatException e) { 364 log("Exception parsing config_retries_per_error_code: " + e); 365 continue; 366 } 367 if (count > 0 && errorCode > 0) { 368 mRetriesLeftPerErrorCode.put(errorCode, count); 369 } 370 } else { 371 log("Exception parsing config_retries_per_error_code: " + c); 372 } 373 } 374 } 375 } 376 377 public boolean restartOnError(int errorCode) { 378 boolean result = false; 379 int retriesLeft = 0; 380 synchronized(mRetriesLeftPerErrorCode) { 381 retriesLeft = mRetriesLeftPerErrorCode.get(errorCode); 382 switch (retriesLeft) { 383 case 0: { 384 // not set, never restart modem 385 break; 386 } 387 case 1: { 388 resetErrorCodeRetries(); 389 result = true; 390 break; 391 } 392 default: { 393 mRetriesLeftPerErrorCode.put(errorCode, retriesLeft - 1); 394 result = false; 395 } 396 } 397 } 398 String str = "ApnContext.restartOnError(" + errorCode + ") found " + retriesLeft + 399 " and returned " + result; 400 if (DBG) log(str); 401 requestLog(str); 402 return result; 403 } 404 405 public int incAndGetConnectionGeneration() { 406 return mConnectionGeneration.incrementAndGet(); 407 } 408 409 public int getConnectionGeneration() { 410 return mConnectionGeneration.get(); 411 } 412 413 @Override 414 public synchronized String toString() { 415 // We don't print mDataConnection because its recursive. 416 return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={" + 417 mWaitingApns + "} mWaitingApnsPermanentFailureCountDown=" + 418 mWaitingApnsPermanentFailureCountDown + " mApnSetting={" + mApnSetting + 419 "} mReason=" + mReason + " mDataEnabled=" + mDataEnabled + " mDependencyMet=" + 420 mDependencyMet + "}"; 421 } 422 423 private void log(String s) { 424 Rlog.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s); 425 } 426 427 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 428 final IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 429 synchronized (mRefCountLock) { 430 pw.println(toString()); 431 if (mRefCount > 0) { 432 pw.increaseIndent(); 433 for (LocalLog l : mLocalLogs) { 434 l.dump(fd, pw, args); 435 } 436 pw.decreaseIndent(); 437 } 438 } 439 } 440} 441