Home | History | Annotate | Download | only in net
      1 /*
      2  * Copyright (C) 2008 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 android.net;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.net.NetworkInfo.DetailedState;
     24 import android.os.Bundle;
     25 import android.os.Handler;
     26 import android.os.Looper;
     27 import android.os.Message;
     28 import android.os.Messenger;
     29 import android.os.RemoteException;
     30 import android.os.ServiceManager;
     31 import android.telephony.PhoneStateListener;
     32 import android.telephony.SignalStrength;
     33 import android.telephony.TelephonyManager;
     34 import android.text.TextUtils;
     35 import android.util.Slog;
     36 
     37 import com.android.internal.telephony.DctConstants;
     38 import com.android.internal.telephony.ITelephony;
     39 import com.android.internal.telephony.PhoneConstants;
     40 import com.android.internal.telephony.TelephonyIntents;
     41 import com.android.internal.util.AsyncChannel;
     42 
     43 import java.io.CharArrayWriter;
     44 import java.io.PrintWriter;
     45 import java.util.concurrent.atomic.AtomicBoolean;
     46 
     47 /**
     48  * Track the state of mobile data connectivity. This is done by
     49  * receiving broadcast intents from the Phone process whenever
     50  * the state of data connectivity changes.
     51  *
     52  * {@hide}
     53  */
     54 public class MobileDataStateTracker extends BaseNetworkStateTracker {
     55 
     56     private static final String TAG = "MobileDataStateTracker";
     57     private static final boolean DBG = true;
     58     private static final boolean VDBG = false;
     59 
     60     private PhoneConstants.DataState mMobileDataState;
     61     private ITelephony mPhoneService;
     62 
     63     private String mApnType;
     64     private NetworkInfo mNetworkInfo;
     65     private boolean mTeardownRequested = false;
     66     private Handler mTarget;
     67     private Context mContext;
     68     private LinkProperties mLinkProperties;
     69     private LinkCapabilities mLinkCapabilities;
     70     private boolean mPrivateDnsRouteSet = false;
     71     private boolean mDefaultRouteSet = false;
     72 
     73     // NOTE: these are only kept for debugging output; actual values are
     74     // maintained in DataConnectionTracker.
     75     protected boolean mUserDataEnabled = true;
     76     protected boolean mPolicyDataEnabled = true;
     77 
     78     private Handler mHandler;
     79     private AsyncChannel mDataConnectionTrackerAc;
     80 
     81     private AtomicBoolean mIsCaptivePortal = new AtomicBoolean(false);
     82 
     83     private SignalStrength mSignalStrength;
     84 
     85     private SamplingDataTracker mSamplingDataTracker = new SamplingDataTracker();
     86 
     87     private static final int UNKNOWN = LinkQualityInfo.UNKNOWN_INT;
     88 
     89     /**
     90      * Create a new MobileDataStateTracker
     91      * @param netType the ConnectivityManager network type
     92      * @param tag the name of this network
     93      */
     94     public MobileDataStateTracker(int netType, String tag) {
     95         mNetworkInfo = new NetworkInfo(netType,
     96                 TelephonyManager.getDefault().getNetworkType(), tag,
     97                 TelephonyManager.getDefault().getNetworkTypeName());
     98         mApnType = networkTypeToApnType(netType);
     99     }
    100 
    101     /**
    102      * Begin monitoring data connectivity.
    103      *
    104      * @param context is the current Android context
    105      * @param target is the Hander to which to return the events.
    106      */
    107     public void startMonitoring(Context context, Handler target) {
    108         mTarget = target;
    109         mContext = context;
    110 
    111         mHandler = new MdstHandler(target.getLooper(), this);
    112 
    113         IntentFilter filter = new IntentFilter();
    114         filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
    115         filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN);
    116         filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
    117 
    118         mContext.registerReceiver(new MobileDataStateReceiver(), filter);
    119         mMobileDataState = PhoneConstants.DataState.DISCONNECTED;
    120 
    121         TelephonyManager tm = (TelephonyManager)mContext.getSystemService(
    122                 Context.TELEPHONY_SERVICE);
    123         tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
    124     }
    125 
    126     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
    127         @Override
    128         public void onSignalStrengthsChanged(SignalStrength signalStrength) {
    129             mSignalStrength = signalStrength;
    130         }
    131     };
    132 
    133     static class MdstHandler extends Handler {
    134         private MobileDataStateTracker mMdst;
    135 
    136         MdstHandler(Looper looper, MobileDataStateTracker mdst) {
    137             super(looper);
    138             mMdst = mdst;
    139         }
    140 
    141         @Override
    142         public void handleMessage(Message msg) {
    143             switch (msg.what) {
    144                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
    145                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
    146                         if (VDBG) {
    147                             mMdst.log("MdstHandler connected");
    148                         }
    149                         mMdst.mDataConnectionTrackerAc = (AsyncChannel) msg.obj;
    150                     } else {
    151                         if (VDBG) {
    152                             mMdst.log("MdstHandler %s NOT connected error=" + msg.arg1);
    153                         }
    154                     }
    155                     break;
    156                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
    157                     if (VDBG) mMdst.log("Disconnected from DataStateTracker");
    158                     mMdst.mDataConnectionTrackerAc = null;
    159                     break;
    160                 default: {
    161                     if (VDBG) mMdst.log("Ignorning unknown message=" + msg);
    162                     break;
    163                 }
    164             }
    165         }
    166     }
    167 
    168     public boolean isPrivateDnsRouteSet() {
    169         return mPrivateDnsRouteSet;
    170     }
    171 
    172     public void privateDnsRouteSet(boolean enabled) {
    173         mPrivateDnsRouteSet = enabled;
    174     }
    175 
    176     public NetworkInfo getNetworkInfo() {
    177         return mNetworkInfo;
    178     }
    179 
    180     public boolean isDefaultRouteSet() {
    181         return mDefaultRouteSet;
    182     }
    183 
    184     public void defaultRouteSet(boolean enabled) {
    185         mDefaultRouteSet = enabled;
    186     }
    187 
    188     /**
    189      * This is not implemented.
    190      */
    191     public void releaseWakeLock() {
    192     }
    193 
    194     private void updateLinkProperitesAndCapatilities(Intent intent) {
    195         mLinkProperties = intent.getParcelableExtra(
    196                 PhoneConstants.DATA_LINK_PROPERTIES_KEY);
    197         if (mLinkProperties == null) {
    198             loge("CONNECTED event did not supply link properties.");
    199             mLinkProperties = new LinkProperties();
    200         }
    201         mLinkProperties.setMtu(mContext.getResources().getInteger(
    202                 com.android.internal.R.integer.config_mobile_mtu));
    203         mLinkCapabilities = intent.getParcelableExtra(
    204                 PhoneConstants.DATA_LINK_CAPABILITIES_KEY);
    205         if (mLinkCapabilities == null) {
    206             loge("CONNECTED event did not supply link capabilities.");
    207             mLinkCapabilities = new LinkCapabilities();
    208         }
    209     }
    210 
    211     private class MobileDataStateReceiver extends BroadcastReceiver {
    212         @Override
    213         public void onReceive(Context context, Intent intent) {
    214             if (intent.getAction().equals(TelephonyIntents.
    215                     ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN)) {
    216                 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
    217                 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
    218                 if (!TextUtils.equals(mApnType, apnType)) {
    219                     return;
    220                 }
    221                 if (DBG) {
    222                     log("Broadcast received: " + intent.getAction() + " apnType=" + apnType
    223                             + " apnName=" + apnName);
    224                 }
    225 
    226                 // Make us in the connecting state until we make a new TYPE_MOBILE_PROVISIONING
    227                 mMobileDataState = PhoneConstants.DataState.CONNECTING;
    228                 updateLinkProperitesAndCapatilities(intent);
    229                 mNetworkInfo.setIsConnectedToProvisioningNetwork(true);
    230 
    231                 // Change state to SUSPENDED so setDetailedState
    232                 // sends EVENT_STATE_CHANGED to connectivityService
    233                 setDetailedState(DetailedState.SUSPENDED, "", apnName);
    234             } else if (intent.getAction().equals(TelephonyIntents.
    235                     ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
    236                 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
    237                 if (VDBG) {
    238                     log(String.format("Broadcast received: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED"
    239                         + "mApnType=%s %s received apnType=%s", mApnType,
    240                         TextUtils.equals(apnType, mApnType) ? "==" : "!=", apnType));
    241                 }
    242                 if (!TextUtils.equals(apnType, mApnType)) {
    243                     return;
    244                 }
    245                 // Assume this isn't a provisioning network.
    246                 mNetworkInfo.setIsConnectedToProvisioningNetwork(false);
    247                 if (DBG) {
    248                     log("Broadcast received: " + intent.getAction() + " apnType=" + apnType);
    249                 }
    250 
    251                 int oldSubtype = mNetworkInfo.getSubtype();
    252                 int newSubType = TelephonyManager.getDefault().getNetworkType();
    253                 String subTypeName = TelephonyManager.getDefault().getNetworkTypeName();
    254                 mNetworkInfo.setSubtype(newSubType, subTypeName);
    255                 if (newSubType != oldSubtype && mNetworkInfo.isConnected()) {
    256                     Message msg = mTarget.obtainMessage(EVENT_NETWORK_SUBTYPE_CHANGED,
    257                                                         oldSubtype, 0, mNetworkInfo);
    258                     msg.sendToTarget();
    259                 }
    260 
    261                 PhoneConstants.DataState state = Enum.valueOf(PhoneConstants.DataState.class,
    262                         intent.getStringExtra(PhoneConstants.STATE_KEY));
    263                 String reason = intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY);
    264                 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
    265                 mNetworkInfo.setRoaming(intent.getBooleanExtra(
    266                         PhoneConstants.DATA_NETWORK_ROAMING_KEY, false));
    267                 if (VDBG) {
    268                     log(mApnType + " setting isAvailable to " +
    269                             intent.getBooleanExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY,false));
    270                 }
    271                 mNetworkInfo.setIsAvailable(!intent.getBooleanExtra(
    272                         PhoneConstants.NETWORK_UNAVAILABLE_KEY, false));
    273 
    274                 if (DBG) {
    275                     log("Received state=" + state + ", old=" + mMobileDataState +
    276                         ", reason=" + (reason == null ? "(unspecified)" : reason));
    277                 }
    278                 if (mMobileDataState != state) {
    279                     mMobileDataState = state;
    280                     switch (state) {
    281                         case DISCONNECTED:
    282                             if(isTeardownRequested()) {
    283                                 setTeardownRequested(false);
    284                             }
    285 
    286                             setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
    287                             // can't do this here - ConnectivityService needs it to clear stuff
    288                             // it's ok though - just leave it to be refreshed next time
    289                             // we connect.
    290                             //if (DBG) log("clearing mInterfaceName for "+ mApnType +
    291                             //        " as it DISCONNECTED");
    292                             //mInterfaceName = null;
    293                             break;
    294                         case CONNECTING:
    295                             setDetailedState(DetailedState.CONNECTING, reason, apnName);
    296                             break;
    297                         case SUSPENDED:
    298                             setDetailedState(DetailedState.SUSPENDED, reason, apnName);
    299                             break;
    300                         case CONNECTED:
    301                             updateLinkProperitesAndCapatilities(intent);
    302                             setDetailedState(DetailedState.CONNECTED, reason, apnName);
    303                             break;
    304                     }
    305 
    306                     if (VDBG) {
    307                         Slog.d(TAG, "TelephonyMgr.DataConnectionStateChanged");
    308                         if (mNetworkInfo != null) {
    309                             Slog.d(TAG, "NetworkInfo = " + mNetworkInfo.toString());
    310                             Slog.d(TAG, "subType = " + String.valueOf(mNetworkInfo.getSubtype()));
    311                             Slog.d(TAG, "subType = " + mNetworkInfo.getSubtypeName());
    312                         }
    313                         if (mLinkProperties != null) {
    314                             Slog.d(TAG, "LinkProperties = " + mLinkProperties.toString());
    315                         } else {
    316                             Slog.d(TAG, "LinkProperties = " );
    317                         }
    318 
    319                         if (mLinkCapabilities != null) {
    320                             Slog.d(TAG, "LinkCapabilities = " + mLinkCapabilities.toString());
    321                         } else {
    322                             Slog.d(TAG, "LinkCapabilities = " );
    323                         }
    324                     }
    325 
    326 
    327                     /* lets not sample traffic data across state changes */
    328                     mSamplingDataTracker.resetSamplingData();
    329                 } else {
    330                     // There was no state change. Check if LinkProperties has been updated.
    331                     if (TextUtils.equals(reason, PhoneConstants.REASON_LINK_PROPERTIES_CHANGED)) {
    332                         mLinkProperties = intent.getParcelableExtra(
    333                                 PhoneConstants.DATA_LINK_PROPERTIES_KEY);
    334                         if (mLinkProperties == null) {
    335                             loge("No link property in LINK_PROPERTIES change event.");
    336                             mLinkProperties = new LinkProperties();
    337                         }
    338                         // Just update reason field in this NetworkInfo
    339                         mNetworkInfo.setDetailedState(mNetworkInfo.getDetailedState(), reason,
    340                                                       mNetworkInfo.getExtraInfo());
    341                         Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED,
    342                                                             mNetworkInfo);
    343                         msg.sendToTarget();
    344                     }
    345                 }
    346             } else if (intent.getAction().
    347                     equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
    348                 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
    349                 if (!TextUtils.equals(apnType, mApnType)) {
    350                     if (DBG) {
    351                         log(String.format(
    352                                 "Broadcast received: ACTION_ANY_DATA_CONNECTION_FAILED ignore, " +
    353                                 "mApnType=%s != received apnType=%s", mApnType, apnType));
    354                     }
    355                     return;
    356                 }
    357                 // Assume this isn't a provisioning network.
    358                 mNetworkInfo.setIsConnectedToProvisioningNetwork(false);
    359                 String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY);
    360                 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
    361                 if (DBG) {
    362                     log("Broadcast received: " + intent.getAction() +
    363                                 " reason=" + reason == null ? "null" : reason);
    364                 }
    365                 setDetailedState(DetailedState.FAILED, reason, apnName);
    366             } else {
    367                 if (DBG) log("Broadcast received: ignore " + intent.getAction());
    368             }
    369         }
    370     }
    371 
    372     private void getPhoneService(boolean forceRefresh) {
    373         if ((mPhoneService == null) || forceRefresh) {
    374             mPhoneService = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
    375         }
    376     }
    377 
    378     /**
    379      * Report whether data connectivity is possible.
    380      */
    381     public boolean isAvailable() {
    382         return mNetworkInfo.isAvailable();
    383     }
    384 
    385     /**
    386      * Return the system properties name associated with the tcp buffer sizes
    387      * for this network.
    388      */
    389     public String getTcpBufferSizesPropName() {
    390         String networkTypeStr = "unknown";
    391         TelephonyManager tm = new TelephonyManager(mContext);
    392         //TODO We have to edit the parameter for getNetworkType regarding CDMA
    393         switch(tm.getNetworkType()) {
    394         case TelephonyManager.NETWORK_TYPE_GPRS:
    395             networkTypeStr = "gprs";
    396             break;
    397         case TelephonyManager.NETWORK_TYPE_EDGE:
    398             networkTypeStr = "edge";
    399             break;
    400         case TelephonyManager.NETWORK_TYPE_UMTS:
    401             networkTypeStr = "umts";
    402             break;
    403         case TelephonyManager.NETWORK_TYPE_HSDPA:
    404             networkTypeStr = "hsdpa";
    405             break;
    406         case TelephonyManager.NETWORK_TYPE_HSUPA:
    407             networkTypeStr = "hsupa";
    408             break;
    409         case TelephonyManager.NETWORK_TYPE_HSPA:
    410             networkTypeStr = "hspa";
    411             break;
    412         case TelephonyManager.NETWORK_TYPE_HSPAP:
    413             networkTypeStr = "hspap";
    414             break;
    415         case TelephonyManager.NETWORK_TYPE_CDMA:
    416             networkTypeStr = "cdma";
    417             break;
    418         case TelephonyManager.NETWORK_TYPE_1xRTT:
    419             networkTypeStr = "1xrtt";
    420             break;
    421         case TelephonyManager.NETWORK_TYPE_EVDO_0:
    422             networkTypeStr = "evdo";
    423             break;
    424         case TelephonyManager.NETWORK_TYPE_EVDO_A:
    425             networkTypeStr = "evdo";
    426             break;
    427         case TelephonyManager.NETWORK_TYPE_EVDO_B:
    428             networkTypeStr = "evdo";
    429             break;
    430         case TelephonyManager.NETWORK_TYPE_IDEN:
    431             networkTypeStr = "iden";
    432             break;
    433         case TelephonyManager.NETWORK_TYPE_LTE:
    434             networkTypeStr = "lte";
    435             break;
    436         case TelephonyManager.NETWORK_TYPE_EHRPD:
    437             networkTypeStr = "ehrpd";
    438             break;
    439         default:
    440             loge("unknown network type: " + tm.getNetworkType());
    441         }
    442         return "net.tcp.buffersize." + networkTypeStr;
    443     }
    444 
    445     /**
    446      * Tear down mobile data connectivity, i.e., disable the ability to create
    447      * mobile data connections.
    448      * TODO - make async and return nothing?
    449      */
    450     public boolean teardown() {
    451         setTeardownRequested(true);
    452         return (setEnableApn(mApnType, false) != PhoneConstants.APN_REQUEST_FAILED);
    453     }
    454 
    455     /**
    456      * @return true if this is ready to operate
    457      */
    458     public boolean isReady() {
    459         return mDataConnectionTrackerAc != null;
    460     }
    461 
    462     @Override
    463     public void captivePortalCheckComplete() {
    464         // not implemented
    465     }
    466 
    467     @Override
    468     public void captivePortalCheckCompleted(boolean isCaptivePortal) {
    469         if (mIsCaptivePortal.getAndSet(isCaptivePortal) != isCaptivePortal) {
    470             // Captive portal change enable/disable failing fast
    471             setEnableFailFastMobileData(
    472                     isCaptivePortal ? DctConstants.ENABLED : DctConstants.DISABLED);
    473         }
    474     }
    475 
    476     /**
    477      * Record the detailed state of a network, and if it is a
    478      * change from the previous state, send a notification to
    479      * any listeners.
    480      * @param state the new {@code DetailedState}
    481      * @param reason a {@code String} indicating a reason for the state change,
    482      * if one was supplied. May be {@code null}.
    483      * @param extraInfo optional {@code String} providing extra information about the state change
    484      */
    485     private void setDetailedState(NetworkInfo.DetailedState state, String reason,
    486             String extraInfo) {
    487         if (DBG) log("setDetailed state, old ="
    488                 + mNetworkInfo.getDetailedState() + " and new state=" + state);
    489         if (state != mNetworkInfo.getDetailedState()) {
    490             boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
    491             String lastReason = mNetworkInfo.getReason();
    492             /*
    493              * If a reason was supplied when the CONNECTING state was entered, and no
    494              * reason was supplied for entering the CONNECTED state, then retain the
    495              * reason that was supplied when going to CONNECTING.
    496              */
    497             if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
    498                     && lastReason != null)
    499                 reason = lastReason;
    500             mNetworkInfo.setDetailedState(state, reason, extraInfo);
    501             Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo));
    502             msg.sendToTarget();
    503         }
    504     }
    505 
    506     public void setTeardownRequested(boolean isRequested) {
    507         mTeardownRequested = isRequested;
    508     }
    509 
    510     public boolean isTeardownRequested() {
    511         return mTeardownRequested;
    512     }
    513 
    514     /**
    515      * Re-enable mobile data connectivity after a {@link #teardown()}.
    516      * TODO - make async and always get a notification?
    517      */
    518     public boolean reconnect() {
    519         boolean retValue = false; //connected or expect to be?
    520         setTeardownRequested(false);
    521         switch (setEnableApn(mApnType, true)) {
    522             case PhoneConstants.APN_ALREADY_ACTIVE:
    523                 // need to set self to CONNECTING so the below message is handled.
    524                 retValue = true;
    525                 break;
    526             case PhoneConstants.APN_REQUEST_STARTED:
    527                 // set IDLE here , avoid the following second FAILED not sent out
    528                 mNetworkInfo.setDetailedState(DetailedState.IDLE, null, null);
    529                 retValue = true;
    530                 break;
    531             case PhoneConstants.APN_REQUEST_FAILED:
    532             case PhoneConstants.APN_TYPE_NOT_AVAILABLE:
    533                 break;
    534             default:
    535                 loge("Error in reconnect - unexpected response.");
    536                 break;
    537         }
    538         return retValue;
    539     }
    540 
    541     /**
    542      * Turn on or off the mobile radio. No connectivity will be possible while the
    543      * radio is off. The operation is a no-op if the radio is already in the desired state.
    544      * @param turnOn {@code true} if the radio should be turned on, {@code false} if
    545      */
    546     public boolean setRadio(boolean turnOn) {
    547         getPhoneService(false);
    548         /*
    549          * If the phone process has crashed in the past, we'll get a
    550          * RemoteException and need to re-reference the service.
    551          */
    552         for (int retry = 0; retry < 2; retry++) {
    553             if (mPhoneService == null) {
    554                 loge("Ignoring mobile radio request because could not acquire PhoneService");
    555                 break;
    556             }
    557 
    558             try {
    559                 return mPhoneService.setRadio(turnOn);
    560             } catch (RemoteException e) {
    561                 if (retry == 0) getPhoneService(true);
    562             }
    563         }
    564 
    565         loge("Could not set radio power to " + (turnOn ? "on" : "off"));
    566         return false;
    567     }
    568 
    569     @Override
    570     public void setUserDataEnable(boolean enabled) {
    571         if (DBG) log("setUserDataEnable: E enabled=" + enabled);
    572         final AsyncChannel channel = mDataConnectionTrackerAc;
    573         if (channel != null) {
    574             channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE,
    575                     enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
    576             mUserDataEnabled = enabled;
    577         }
    578         if (VDBG) log("setUserDataEnable: X enabled=" + enabled);
    579     }
    580 
    581     @Override
    582     public void setPolicyDataEnable(boolean enabled) {
    583         if (DBG) log("setPolicyDataEnable(enabled=" + enabled + ")");
    584         final AsyncChannel channel = mDataConnectionTrackerAc;
    585         if (channel != null) {
    586             channel.sendMessage(DctConstants.CMD_SET_POLICY_DATA_ENABLE,
    587                     enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
    588             mPolicyDataEnabled = enabled;
    589         }
    590     }
    591 
    592     /**
    593      * Eanble/disable FailFast
    594      *
    595      * @param enabled is DctConstants.ENABLED/DISABLED
    596      */
    597     public void setEnableFailFastMobileData(int enabled) {
    598         if (DBG) log("setEnableFailFastMobileData(enabled=" + enabled + ")");
    599         final AsyncChannel channel = mDataConnectionTrackerAc;
    600         if (channel != null) {
    601             channel.sendMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled);
    602         }
    603     }
    604 
    605     /**
    606      * carrier dependency is met/unmet
    607      * @param met
    608      */
    609     public void setDependencyMet(boolean met) {
    610         Bundle bundle = Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType);
    611         try {
    612             if (DBG) log("setDependencyMet: E met=" + met);
    613             Message msg = Message.obtain();
    614             msg.what = DctConstants.CMD_SET_DEPENDENCY_MET;
    615             msg.arg1 = (met ? DctConstants.ENABLED : DctConstants.DISABLED);
    616             msg.setData(bundle);
    617             mDataConnectionTrackerAc.sendMessage(msg);
    618             if (VDBG) log("setDependencyMet: X met=" + met);
    619         } catch (NullPointerException e) {
    620             loge("setDependencyMet: X mAc was null" + e);
    621         }
    622     }
    623 
    624     /**
    625      *  Inform DCT mobile provisioning has started, it ends when provisioning completes.
    626      */
    627     public void enableMobileProvisioning(String url) {
    628         if (DBG) log("enableMobileProvisioning(url=" + url + ")");
    629         final AsyncChannel channel = mDataConnectionTrackerAc;
    630         if (channel != null) {
    631             Message msg = Message.obtain();
    632             msg.what = DctConstants.CMD_ENABLE_MOBILE_PROVISIONING;
    633             msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, url));
    634             channel.sendMessage(msg);
    635         }
    636     }
    637 
    638     /**
    639      * Return if this network is the provisioning network. Valid only if connected.
    640      * @param met
    641      */
    642     public boolean isProvisioningNetwork() {
    643         boolean retVal;
    644         try {
    645             Message msg = Message.obtain();
    646             msg.what = DctConstants.CMD_IS_PROVISIONING_APN;
    647             msg.setData(Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType));
    648             Message result = mDataConnectionTrackerAc.sendMessageSynchronously(msg);
    649             retVal = result.arg1 == DctConstants.ENABLED;
    650         } catch (NullPointerException e) {
    651             loge("isProvisioningNetwork: X " + e);
    652             retVal = false;
    653         }
    654         if (DBG) log("isProvisioningNetwork: retVal=" + retVal);
    655         return retVal;
    656     }
    657 
    658     @Override
    659     public void addStackedLink(LinkProperties link) {
    660         mLinkProperties.addStackedLink(link);
    661     }
    662 
    663     @Override
    664     public void removeStackedLink(LinkProperties link) {
    665         mLinkProperties.removeStackedLink(link);
    666     }
    667 
    668     @Override
    669     public String toString() {
    670         final CharArrayWriter writer = new CharArrayWriter();
    671         final PrintWriter pw = new PrintWriter(writer);
    672         pw.print("Mobile data state: "); pw.println(mMobileDataState);
    673         pw.print("Data enabled: user="); pw.print(mUserDataEnabled);
    674         pw.print(", policy="); pw.println(mPolicyDataEnabled);
    675         return writer.toString();
    676     }
    677 
    678     /**
    679      * Internal method supporting the ENABLE_MMS feature.
    680      * @param apnType the type of APN to be enabled or disabled (e.g., mms)
    681      * @param enable {@code true} to enable the specified APN type,
    682      * {@code false} to disable it.
    683      * @return an integer value representing the outcome of the request.
    684      */
    685     private int setEnableApn(String apnType, boolean enable) {
    686         getPhoneService(false);
    687         /*
    688          * If the phone process has crashed in the past, we'll get a
    689          * RemoteException and need to re-reference the service.
    690          */
    691         for (int retry = 0; retry < 2; retry++) {
    692             if (mPhoneService == null) {
    693                 loge("Ignoring feature request because could not acquire PhoneService");
    694                 break;
    695             }
    696 
    697             try {
    698                 if (enable) {
    699                     return mPhoneService.enableApnType(apnType);
    700                 } else {
    701                     return mPhoneService.disableApnType(apnType);
    702                 }
    703             } catch (RemoteException e) {
    704                 if (retry == 0) getPhoneService(true);
    705             }
    706         }
    707 
    708         loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\"");
    709         return PhoneConstants.APN_REQUEST_FAILED;
    710     }
    711 
    712     public static String networkTypeToApnType(int netType) {
    713         switch(netType) {
    714             case ConnectivityManager.TYPE_MOBILE:
    715                 return PhoneConstants.APN_TYPE_DEFAULT;  // TODO - use just one of these
    716             case ConnectivityManager.TYPE_MOBILE_MMS:
    717                 return PhoneConstants.APN_TYPE_MMS;
    718             case ConnectivityManager.TYPE_MOBILE_SUPL:
    719                 return PhoneConstants.APN_TYPE_SUPL;
    720             case ConnectivityManager.TYPE_MOBILE_DUN:
    721                 return PhoneConstants.APN_TYPE_DUN;
    722             case ConnectivityManager.TYPE_MOBILE_HIPRI:
    723                 return PhoneConstants.APN_TYPE_HIPRI;
    724             case ConnectivityManager.TYPE_MOBILE_FOTA:
    725                 return PhoneConstants.APN_TYPE_FOTA;
    726             case ConnectivityManager.TYPE_MOBILE_IMS:
    727                 return PhoneConstants.APN_TYPE_IMS;
    728             case ConnectivityManager.TYPE_MOBILE_CBS:
    729                 return PhoneConstants.APN_TYPE_CBS;
    730             case ConnectivityManager.TYPE_MOBILE_IA:
    731                 return PhoneConstants.APN_TYPE_IA;
    732             default:
    733                 sloge("Error mapping networkType " + netType + " to apnType.");
    734                 return null;
    735         }
    736     }
    737 
    738 
    739     /**
    740      * @see android.net.NetworkStateTracker#getLinkProperties()
    741      */
    742     @Override
    743     public LinkProperties getLinkProperties() {
    744         return new LinkProperties(mLinkProperties);
    745     }
    746 
    747     /**
    748      * @see android.net.NetworkStateTracker#getLinkCapabilities()
    749      */
    750     @Override
    751     public LinkCapabilities getLinkCapabilities() {
    752         return new LinkCapabilities(mLinkCapabilities);
    753     }
    754 
    755     public void supplyMessenger(Messenger messenger) {
    756         if (VDBG) log(mApnType + " got supplyMessenger");
    757         AsyncChannel ac = new AsyncChannel();
    758         ac.connect(mContext, MobileDataStateTracker.this.mHandler, messenger);
    759     }
    760 
    761     private void log(String s) {
    762         Slog.d(TAG, mApnType + ": " + s);
    763     }
    764 
    765     private void loge(String s) {
    766         Slog.e(TAG, mApnType + ": " + s);
    767     }
    768 
    769     static private void sloge(String s) {
    770         Slog.e(TAG, s);
    771     }
    772 
    773     @Override
    774     public LinkQualityInfo getLinkQualityInfo() {
    775         if (mNetworkInfo == null || mNetworkInfo.getType() == ConnectivityManager.TYPE_NONE) {
    776             // no data available yet; just return
    777             return null;
    778         }
    779 
    780         MobileLinkQualityInfo li = new MobileLinkQualityInfo();
    781 
    782         li.setNetworkType(mNetworkInfo.getType());
    783 
    784         mSamplingDataTracker.setCommonLinkQualityInfoFields(li);
    785 
    786         if (mNetworkInfo.getSubtype() != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
    787             li.setMobileNetworkType(mNetworkInfo.getSubtype());
    788 
    789             NetworkDataEntry entry = getNetworkDataEntry(mNetworkInfo.getSubtype());
    790             if (entry != null) {
    791                 li.setTheoreticalRxBandwidth(entry.downloadBandwidth);
    792                 li.setTheoreticalRxBandwidth(entry.uploadBandwidth);
    793                 li.setTheoreticalLatency(entry.latency);
    794             }
    795 
    796             if (mSignalStrength != null) {
    797                 li.setNormalizedSignalStrength(getNormalizedSignalStrength(
    798                         li.getMobileNetworkType(), mSignalStrength));
    799             }
    800         }
    801 
    802         SignalStrength ss = mSignalStrength;
    803         if (ss != null) {
    804 
    805             li.setRssi(ss.getGsmSignalStrength());
    806             li.setGsmErrorRate(ss.getGsmBitErrorRate());
    807             li.setCdmaDbm(ss.getCdmaDbm());
    808             li.setCdmaEcio(ss.getCdmaEcio());
    809             li.setEvdoDbm(ss.getEvdoDbm());
    810             li.setEvdoEcio(ss.getEvdoEcio());
    811             li.setEvdoSnr(ss.getEvdoSnr());
    812             li.setLteSignalStrength(ss.getLteSignalStrength());
    813             li.setLteRsrp(ss.getLteRsrp());
    814             li.setLteRsrq(ss.getLteRsrq());
    815             li.setLteRssnr(ss.getLteRssnr());
    816             li.setLteCqi(ss.getLteCqi());
    817         }
    818 
    819         if (VDBG) {
    820             Slog.d(TAG, "Returning LinkQualityInfo with"
    821                     + " MobileNetworkType = " + String.valueOf(li.getMobileNetworkType())
    822                     + " Theoretical Rx BW = " + String.valueOf(li.getTheoreticalRxBandwidth())
    823                     + " gsm Signal Strength = " + String.valueOf(li.getRssi())
    824                     + " cdma Signal Strength = " + String.valueOf(li.getCdmaDbm())
    825                     + " evdo Signal Strength = " + String.valueOf(li.getEvdoDbm())
    826                     + " Lte Signal Strength = " + String.valueOf(li.getLteSignalStrength()));
    827         }
    828 
    829         return li;
    830     }
    831 
    832     static class NetworkDataEntry {
    833         public int networkType;
    834         public int downloadBandwidth;               // in kbps
    835         public int uploadBandwidth;                 // in kbps
    836         public int latency;                         // in millisecond
    837 
    838         NetworkDataEntry(int i1, int i2, int i3, int i4) {
    839             networkType = i1;
    840             downloadBandwidth = i2;
    841             uploadBandwidth = i3;
    842             latency = i4;
    843         }
    844     }
    845 
    846     private static NetworkDataEntry [] mTheoreticalBWTable = new NetworkDataEntry[] {
    847             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EDGE,      237,     118, UNKNOWN),
    848             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_GPRS,       48,      40, UNKNOWN),
    849             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_UMTS,      384,      64, UNKNOWN),
    850             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSDPA,   14400, UNKNOWN, UNKNOWN),
    851             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSUPA,   14400,    5760, UNKNOWN),
    852             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPA,    14400,    5760, UNKNOWN),
    853             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPAP,   21000,    5760, UNKNOWN),
    854             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_CDMA,  UNKNOWN, UNKNOWN, UNKNOWN),
    855             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_1xRTT, UNKNOWN, UNKNOWN, UNKNOWN),
    856             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_0,   2468,     153, UNKNOWN),
    857             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_A,   3072,    1800, UNKNOWN),
    858             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_B,  14700,    1800, UNKNOWN),
    859             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_IDEN,  UNKNOWN, UNKNOWN, UNKNOWN),
    860             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_LTE,    100000,   50000, UNKNOWN),
    861             new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EHRPD, UNKNOWN, UNKNOWN, UNKNOWN),
    862     };
    863 
    864     private static NetworkDataEntry getNetworkDataEntry(int networkType) {
    865         for (NetworkDataEntry entry : mTheoreticalBWTable) {
    866             if (entry.networkType == networkType) {
    867                 return entry;
    868             }
    869         }
    870 
    871         Slog.e(TAG, "Could not find Theoretical BW entry for " + String.valueOf(networkType));
    872         return null;
    873     }
    874 
    875     private static int getNormalizedSignalStrength(int networkType, SignalStrength ss) {
    876 
    877         int level;
    878 
    879         switch(networkType) {
    880             case TelephonyManager.NETWORK_TYPE_EDGE:
    881             case TelephonyManager.NETWORK_TYPE_GPRS:
    882             case TelephonyManager.NETWORK_TYPE_UMTS:
    883             case TelephonyManager.NETWORK_TYPE_HSDPA:
    884             case TelephonyManager.NETWORK_TYPE_HSUPA:
    885             case TelephonyManager.NETWORK_TYPE_HSPA:
    886             case TelephonyManager.NETWORK_TYPE_HSPAP:
    887                 level = ss.getGsmLevel();
    888                 break;
    889             case TelephonyManager.NETWORK_TYPE_CDMA:
    890             case TelephonyManager.NETWORK_TYPE_1xRTT:
    891                 level = ss.getCdmaLevel();
    892                 break;
    893             case TelephonyManager.NETWORK_TYPE_EVDO_0:
    894             case TelephonyManager.NETWORK_TYPE_EVDO_A:
    895             case TelephonyManager.NETWORK_TYPE_EVDO_B:
    896                 level = ss.getEvdoLevel();
    897                 break;
    898             case TelephonyManager.NETWORK_TYPE_LTE:
    899                 level = ss.getLteLevel();
    900                 break;
    901             case TelephonyManager.NETWORK_TYPE_IDEN:
    902             case TelephonyManager.NETWORK_TYPE_EHRPD:
    903             default:
    904                 return UNKNOWN;
    905         }
    906 
    907         return (level * LinkQualityInfo.NORMALIZED_SIGNAL_STRENGTH_RANGE) /
    908                 SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
    909     }
    910 
    911     @Override
    912     public void startSampling(SamplingDataTracker.SamplingSnapshot s) {
    913         mSamplingDataTracker.startSampling(s);
    914     }
    915 
    916     @Override
    917     public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
    918         mSamplingDataTracker.stopSampling(s);
    919     }
    920 }
    921