blob: ea2144dbbf57976fc5d19591277136e3daecf251 [file] [log] [blame]
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.telephony.dataconnection;
import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.net.NetworkConfig;
import android.telephony.Rlog;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Maintain the Apn context
*/
public class ApnContext {
public final String LOG_TAG;
protected static final boolean DBG = false;
private final Context mContext;
private final String mApnType;
private DctConstants.State mState;
private ArrayList<ApnSetting> mWaitingApns = null;
/**
* Used to check if conditions (new RAT) are resulting in a new list which warrants a retry.
* Set in the last trySetupData call.
*/
private ArrayList<ApnSetting> mOriginalWaitingApns = null;
public final int priority;
/** A zero indicates that all waiting APNs had a permanent error */
private AtomicInteger mWaitingApnsPermanentFailureCountDown;
private ApnSetting mApnSetting;
DcAsyncChannel mDcAc;
String mReason;
PendingIntent mReconnectAlarmIntent;
/**
* user/app requested connection on this APN
*/
AtomicBoolean mDataEnabled;
private final Object mRefCountLock = new Object();
private int mRefCount = 0;
/**
* carrier requirements met
*/
AtomicBoolean mDependencyMet;
private final DcTrackerBase mDcTracker;
/**
* Remember this as a change in this value to a more permissive state
* should cause us to retry even permanent failures
*/
private boolean mConcurrentVoiceAndDataAllowed;
/**
* used to track a single connection request so disconnects can get ignored if
* obsolete.
*/
private final AtomicInteger mConnectionGeneration = new AtomicInteger(0);
public ApnContext(Context context, String apnType, String logTag, NetworkConfig config,
DcTrackerBase tracker) {
mContext = context;
mApnType = apnType;
mState = DctConstants.State.IDLE;
setReason(Phone.REASON_DATA_ENABLED);
mDataEnabled = new AtomicBoolean(false);
mDependencyMet = new AtomicBoolean(config.dependencyMet);
mWaitingApnsPermanentFailureCountDown = new AtomicInteger(0);
priority = config.priority;
LOG_TAG = logTag;
mDcTracker = tracker;
}
public String getApnType() {
return mApnType;
}
public synchronized DcAsyncChannel getDcAc() {
return mDcAc;
}
public synchronized void setDataConnectionAc(DcAsyncChannel dcac) {
if (DBG) {
log("setDataConnectionAc: old dcac=" + mDcAc + " new dcac=" + dcac
+ " this=" + this);
}
mDcAc = dcac;
}
public synchronized void releaseDataConnection(String reason) {
if (mDcAc != null) {
mDcAc.tearDown(this, reason, null);
mDcAc = null;
}
setState(DctConstants.State.IDLE);
}
public synchronized PendingIntent getReconnectIntent() {
return mReconnectAlarmIntent;
}
public synchronized void setReconnectIntent(PendingIntent intent) {
mReconnectAlarmIntent = intent;
}
public synchronized ApnSetting getApnSetting() {
if (DBG) log("getApnSetting: apnSetting=" + mApnSetting);
return mApnSetting;
}
public synchronized void setApnSetting(ApnSetting apnSetting) {
if (DBG) log("setApnSetting: apnSetting=" + apnSetting);
mApnSetting = apnSetting;
}
public synchronized void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
mWaitingApns = waitingApns;
mOriginalWaitingApns = new ArrayList<ApnSetting>(waitingApns);
mWaitingApnsPermanentFailureCountDown.set(mWaitingApns.size());
}
public int getWaitingApnsPermFailCount() {
return mWaitingApnsPermanentFailureCountDown.get();
}
public void decWaitingApnsPermFailCount() {
mWaitingApnsPermanentFailureCountDown.decrementAndGet();
}
public synchronized ApnSetting getNextWaitingApn() {
ArrayList<ApnSetting> list = mWaitingApns;
ApnSetting apn = null;
if (list != null) {
if (!list.isEmpty()) {
apn = list.get(0);
}
}
return apn;
}
public synchronized void removeWaitingApn(ApnSetting apn) {
if (mWaitingApns != null) {
mWaitingApns.remove(apn);
}
}
public synchronized ArrayList<ApnSetting> getOriginalWaitingApns() {
return mOriginalWaitingApns;
}
public synchronized ArrayList<ApnSetting> getWaitingApns() {
return mWaitingApns;
}
public synchronized void setConcurrentVoiceAndDataAllowed(boolean allowed) {
mConcurrentVoiceAndDataAllowed = allowed;
}
public synchronized boolean isConcurrentVoiceAndDataAllowed() {
return mConcurrentVoiceAndDataAllowed;
}
public synchronized void setState(DctConstants.State s) {
if (DBG) {
log("setState: " + s + ", previous state:" + mState);
}
mState = s;
if (mState == DctConstants.State.FAILED) {
if (mWaitingApns != null) {
mWaitingApns.clear(); // when teardown the connection and set to IDLE
}
}
}
public synchronized DctConstants.State getState() {
return mState;
}
public boolean isDisconnected() {
DctConstants.State currentState = getState();
return ((currentState == DctConstants.State.IDLE) ||
currentState == DctConstants.State.FAILED);
}
public synchronized void setReason(String reason) {
if (DBG) {
log("set reason as " + reason + ",current state " + mState);
}
mReason = reason;
}
public synchronized String getReason() {
return mReason;
}
public boolean isReady() {
return mDataEnabled.get() && mDependencyMet.get();
}
public boolean isConnectable() {
return isReady() && ((mState == DctConstants.State.IDLE)
|| (mState == DctConstants.State.SCANNING)
|| (mState == DctConstants.State.RETRYING)
|| (mState == DctConstants.State.FAILED));
}
public boolean isConnectedOrConnecting() {
return isReady() && ((mState == DctConstants.State.CONNECTED)
|| (mState == DctConstants.State.CONNECTING)
|| (mState == DctConstants.State.SCANNING)
|| (mState == DctConstants.State.RETRYING));
}
public void setEnabled(boolean enabled) {
if (DBG) {
log("set enabled as " + enabled + ", current state is " + mDataEnabled.get());
}
mDataEnabled.set(enabled);
}
public boolean isEnabled() {
return mDataEnabled.get();
}
public void setDependencyMet(boolean met) {
if (DBG) {
log("set mDependencyMet as " + met + " current state is " + mDependencyMet.get());
}
mDependencyMet.set(met);
}
public boolean getDependencyMet() {
return mDependencyMet.get();
}
public boolean isProvisioningApn() {
String provisioningApn = mContext.getResources()
.getString(R.string.mobile_provisioning_apn);
if (!TextUtils.isEmpty(provisioningApn) &&
(mApnSetting != null) && (mApnSetting.apn != null)) {
return (mApnSetting.apn.equals(provisioningApn));
} else {
return false;
}
}
private final ArrayList<LocalLog> mLocalLogs = new ArrayList<LocalLog>();
public void requestLog(String str) {
synchronized (mRefCountLock) {
for (LocalLog l : mLocalLogs) {
l.log(str);
}
}
}
public void incRefCount(LocalLog log) {
synchronized (mRefCountLock) {
if (mRefCount == 0) {
// we wanted to leave the last in so it could actually capture the tear down
// of the network
requestLog("clearing log with size=" + mLocalLogs.size());
mLocalLogs.clear();
}
if (mLocalLogs.contains(log)) {
log.log("ApnContext.incRefCount has duplicate add - " + mRefCount);
} else {
mLocalLogs.add(log);
log.log("ApnContext.incRefCount - " + mRefCount);
}
if (mRefCount++ == 0) {
mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), true);
}
}
}
public void decRefCount(LocalLog log) {
synchronized (mRefCountLock) {
// leave the last log alive to capture the actual tear down
if (mRefCount != 1) {
if (mLocalLogs.remove(log)) {
log.log("ApnContext.decRefCount - " + mRefCount);
} else {
log.log("ApnContext.decRefCount didn't find log - " + mRefCount);
}
} else {
log.log("ApnContext.decRefCount - 1");
}
if (mRefCount-- == 1) {
mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), false);
}
if (mRefCount < 0) {
log.log("ApnContext.decRefCount went to " + mRefCount);
mRefCount = 0;
}
}
}
private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray();
public void resetErrorCodeRetries() {
requestLog("ApnContext.resetErrorCodeRetries");
if (DBG) log("ApnContext.resetErrorCodeRetries");
String[] config = Resources.getSystem().getStringArray(
com.android.internal.R.array.config_cell_retries_per_error_code);
synchronized (mRetriesLeftPerErrorCode) {
mRetriesLeftPerErrorCode.clear();
for (String c : config) {
String errorValue[] = c.split(",");
if (errorValue != null && errorValue.length == 2) {
int count = 0;
int errorCode = 0;
try {
errorCode = Integer.parseInt(errorValue[0]);
count = Integer.parseInt(errorValue[1]);
} catch (NumberFormatException e) {
log("Exception parsing config_retries_per_error_code: " + e);
continue;
}
if (count > 0 && errorCode > 0) {
mRetriesLeftPerErrorCode.put(errorCode, count);
}
} else {
log("Exception parsing config_retries_per_error_code: " + c);
}
}
}
}
public boolean restartOnError(int errorCode) {
boolean result = false;
int retriesLeft = 0;
synchronized(mRetriesLeftPerErrorCode) {
retriesLeft = mRetriesLeftPerErrorCode.get(errorCode);
switch (retriesLeft) {
case 0: {
// not set, never restart modem
break;
}
case 1: {
resetErrorCodeRetries();
result = true;
break;
}
default: {
mRetriesLeftPerErrorCode.put(errorCode, retriesLeft - 1);
result = false;
}
}
}
String str = "ApnContext.restartOnError(" + errorCode + ") found " + retriesLeft +
" and returned " + result;
if (DBG) log(str);
requestLog(str);
return result;
}
public int incAndGetConnectionGeneration() {
return mConnectionGeneration.incrementAndGet();
}
public int getConnectionGeneration() {
return mConnectionGeneration.get();
}
@Override
public synchronized String toString() {
// We don't print mDataConnection because its recursive.
return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={" +
mWaitingApns + "} mWaitingApnsPermanentFailureCountDown=" +
mWaitingApnsPermanentFailureCountDown + " mApnSetting={" + mApnSetting +
"} mReason=" + mReason + " mDataEnabled=" + mDataEnabled + " mDependencyMet=" +
mDependencyMet + "}";
}
private void log(String s) {
Rlog.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s);
}
public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
synchronized (mRefCountLock) {
pw.println(toString());
if (mRefCount > 0) {
pw.increaseIndent();
for (LocalLog l : mLocalLogs) {
l.dump(fd, pw, args);
}
pw.decreaseIndent();
}
}
}
}