Home | History | Annotate | Download | only in dataconnection
      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