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