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