OSDN Git Service

adad8decca8979e482815f2e5f385c676ff546d4
[android-x86/frameworks-base.git] / core / java / android / net / MobileDataStateTracker.java
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.TelephonyManager;
32 import android.text.TextUtils;
33 import android.util.Slog;
34
35 import com.android.internal.telephony.DctConstants;
36 import com.android.internal.telephony.ITelephony;
37 import com.android.internal.telephony.PhoneConstants;
38 import com.android.internal.telephony.TelephonyIntents;
39 import com.android.internal.util.AsyncChannel;
40
41 import java.io.CharArrayWriter;
42 import java.io.PrintWriter;
43 import java.util.concurrent.atomic.AtomicBoolean;
44
45 /**
46  * Track the state of mobile data connectivity. This is done by
47  * receiving broadcast intents from the Phone process whenever
48  * the state of data connectivity changes.
49  *
50  * {@hide}
51  */
52 public class MobileDataStateTracker implements NetworkStateTracker {
53
54     private static final String TAG = "MobileDataStateTracker";
55     private static final boolean DBG = false;
56     private static final boolean VDBG = false;
57
58     private PhoneConstants.DataState mMobileDataState;
59     private ITelephony mPhoneService;
60
61     private String mApnType;
62     private NetworkInfo mNetworkInfo;
63     private boolean mTeardownRequested = false;
64     private Handler mTarget;
65     private Context mContext;
66     private LinkProperties mLinkProperties;
67     private LinkCapabilities mLinkCapabilities;
68     private boolean mPrivateDnsRouteSet = false;
69     private boolean mDefaultRouteSet = false;
70
71     // NOTE: these are only kept for debugging output; actual values are
72     // maintained in DataConnectionTracker.
73     protected boolean mUserDataEnabled = true;
74     protected boolean mPolicyDataEnabled = true;
75
76     private Handler mHandler;
77     private AsyncChannel mDataConnectionTrackerAc;
78
79     private AtomicBoolean mIsCaptivePortal = new AtomicBoolean(false);
80
81     /**
82      * Create a new MobileDataStateTracker
83      * @param netType the ConnectivityManager network type
84      * @param tag the name of this network
85      */
86     public MobileDataStateTracker(int netType, String tag) {
87         mNetworkInfo = new NetworkInfo(netType,
88                 TelephonyManager.getDefault().getNetworkType(), tag,
89                 TelephonyManager.getDefault().getNetworkTypeName());
90         mApnType = networkTypeToApnType(netType);
91     }
92
93     /**
94      * Begin monitoring data connectivity.
95      *
96      * @param context is the current Android context
97      * @param target is the Hander to which to return the events.
98      */
99     public void startMonitoring(Context context, Handler target) {
100         mTarget = target;
101         mContext = context;
102
103         mHandler = new MdstHandler(target.getLooper(), this);
104
105         IntentFilter filter = new IntentFilter();
106         filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
107         filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
108
109         mContext.registerReceiver(new MobileDataStateReceiver(), filter);
110         mMobileDataState = PhoneConstants.DataState.DISCONNECTED;
111     }
112
113     static class MdstHandler extends Handler {
114         private MobileDataStateTracker mMdst;
115
116         MdstHandler(Looper looper, MobileDataStateTracker mdst) {
117             super(looper);
118             mMdst = mdst;
119         }
120
121         @Override
122         public void handleMessage(Message msg) {
123             switch (msg.what) {
124                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
125                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
126                         if (VDBG) {
127                             mMdst.log("MdstHandler connected");
128                         }
129                         mMdst.mDataConnectionTrackerAc = (AsyncChannel) msg.obj;
130                     } else {
131                         if (VDBG) {
132                             mMdst.log("MdstHandler %s NOT connected error=" + msg.arg1);
133                         }
134                     }
135                     break;
136                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
137                     if (VDBG) mMdst.log("Disconnected from DataStateTracker");
138                     mMdst.mDataConnectionTrackerAc = null;
139                     break;
140                 default: {
141                     if (VDBG) mMdst.log("Ignorning unknown message=" + msg);
142                     break;
143                 }
144             }
145         }
146     }
147
148     public boolean isPrivateDnsRouteSet() {
149         return mPrivateDnsRouteSet;
150     }
151
152     public void privateDnsRouteSet(boolean enabled) {
153         mPrivateDnsRouteSet = enabled;
154     }
155
156     public NetworkInfo getNetworkInfo() {
157         return mNetworkInfo;
158     }
159
160     public boolean isDefaultRouteSet() {
161         return mDefaultRouteSet;
162     }
163
164     public void defaultRouteSet(boolean enabled) {
165         mDefaultRouteSet = enabled;
166     }
167
168     /**
169      * This is not implemented.
170      */
171     public void releaseWakeLock() {
172     }
173
174     private class MobileDataStateReceiver extends BroadcastReceiver {
175         @Override
176         public void onReceive(Context context, Intent intent) {
177             if (intent.getAction().equals(TelephonyIntents.
178                     ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
179                 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
180                 if (VDBG) {
181                     log(String.format("Broadcast received: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED"
182                         + "mApnType=%s %s received apnType=%s", mApnType,
183                         TextUtils.equals(apnType, mApnType) ? "==" : "!=", apnType));
184                 }
185                 if (!TextUtils.equals(apnType, mApnType)) {
186                     return;
187                 }
188
189                 int oldSubtype = mNetworkInfo.getSubtype();
190                 int newSubType = TelephonyManager.getDefault().getNetworkType();
191                 String subTypeName = TelephonyManager.getDefault().getNetworkTypeName();
192                 mNetworkInfo.setSubtype(newSubType, subTypeName);
193                 if (newSubType != oldSubtype && mNetworkInfo.isConnected()) {
194                     Message msg = mTarget.obtainMessage(EVENT_NETWORK_SUBTYPE_CHANGED,
195                                                         oldSubtype, 0, mNetworkInfo);
196                     msg.sendToTarget();
197                 }
198
199                 PhoneConstants.DataState state = Enum.valueOf(PhoneConstants.DataState.class,
200                         intent.getStringExtra(PhoneConstants.STATE_KEY));
201                 String reason = intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY);
202                 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
203                 mNetworkInfo.setRoaming(intent.getBooleanExtra(
204                         PhoneConstants.DATA_NETWORK_ROAMING_KEY, false));
205                 if (VDBG) {
206                     log(mApnType + " setting isAvailable to " +
207                             intent.getBooleanExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY,false));
208                 }
209                 mNetworkInfo.setIsAvailable(!intent.getBooleanExtra(
210                         PhoneConstants.NETWORK_UNAVAILABLE_KEY, false));
211
212                 if (DBG) {
213                     log("Received state=" + state + ", old=" + mMobileDataState +
214                         ", reason=" + (reason == null ? "(unspecified)" : reason));
215                 }
216                 if (mMobileDataState != state) {
217                     mMobileDataState = state;
218                     switch (state) {
219                         case DISCONNECTED:
220                             if(isTeardownRequested()) {
221                                 setTeardownRequested(false);
222                             }
223
224                             setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
225                             // can't do this here - ConnectivityService needs it to clear stuff
226                             // it's ok though - just leave it to be refreshed next time
227                             // we connect.
228                             //if (DBG) log("clearing mInterfaceName for "+ mApnType +
229                             //        " as it DISCONNECTED");
230                             //mInterfaceName = null;
231                             break;
232                         case CONNECTING:
233                             setDetailedState(DetailedState.CONNECTING, reason, apnName);
234                             break;
235                         case SUSPENDED:
236                             setDetailedState(DetailedState.SUSPENDED, reason, apnName);
237                             break;
238                         case CONNECTED:
239                             mLinkProperties = intent.getParcelableExtra(
240                                     PhoneConstants.DATA_LINK_PROPERTIES_KEY);
241                             if (mLinkProperties == null) {
242                                 loge("CONNECTED event did not supply link properties.");
243                                 mLinkProperties = new LinkProperties();
244                             }
245                             mLinkCapabilities = intent.getParcelableExtra(
246                                     PhoneConstants.DATA_LINK_CAPABILITIES_KEY);
247                             if (mLinkCapabilities == null) {
248                                 loge("CONNECTED event did not supply link capabilities.");
249                                 mLinkCapabilities = new LinkCapabilities();
250                             }
251                             setDetailedState(DetailedState.CONNECTED, reason, apnName);
252                             break;
253                     }
254                 } else {
255                     // There was no state change. Check if LinkProperties has been updated.
256                     if (TextUtils.equals(reason, PhoneConstants.REASON_LINK_PROPERTIES_CHANGED)) {
257                         mLinkProperties = intent.getParcelableExtra(
258                                 PhoneConstants.DATA_LINK_PROPERTIES_KEY);
259                         if (mLinkProperties == null) {
260                             loge("No link property in LINK_PROPERTIES change event.");
261                             mLinkProperties = new LinkProperties();
262                         }
263                         // Just update reason field in this NetworkInfo
264                         mNetworkInfo.setDetailedState(mNetworkInfo.getDetailedState(), reason,
265                                                       mNetworkInfo.getExtraInfo());
266                         Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED,
267                                                             mNetworkInfo);
268                         msg.sendToTarget();
269                     }
270                 }
271             } else if (intent.getAction().
272                     equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
273                 String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
274                 if (!TextUtils.equals(apnType, mApnType)) {
275                     if (DBG) {
276                         log(String.format(
277                                 "Broadcast received: ACTION_ANY_DATA_CONNECTION_FAILED ignore, " +
278                                 "mApnType=%s != received apnType=%s", mApnType, apnType));
279                     }
280                     return;
281                 }
282                 String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY);
283                 String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
284                 if (DBG) {
285                     log("Received " + intent.getAction() +
286                                 " broadcast" + reason == null ? "" : "(" + reason + ")");
287                 }
288                 setDetailedState(DetailedState.FAILED, reason, apnName);
289             } else {
290                 if (DBG) log("Broadcast received: ignore " + intent.getAction());
291             }
292         }
293     }
294
295     private void getPhoneService(boolean forceRefresh) {
296         if ((mPhoneService == null) || forceRefresh) {
297             mPhoneService = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
298         }
299     }
300
301     /**
302      * Report whether data connectivity is possible.
303      */
304     public boolean isAvailable() {
305         return mNetworkInfo.isAvailable();
306     }
307
308     /**
309      * Return the system properties name associated with the tcp buffer sizes
310      * for this network.
311      */
312     public String getTcpBufferSizesPropName() {
313         String networkTypeStr = "unknown";
314         TelephonyManager tm = new TelephonyManager(mContext);
315         //TODO We have to edit the parameter for getNetworkType regarding CDMA
316         switch(tm.getNetworkType()) {
317         case TelephonyManager.NETWORK_TYPE_GPRS:
318             networkTypeStr = "gprs";
319             break;
320         case TelephonyManager.NETWORK_TYPE_EDGE:
321             networkTypeStr = "edge";
322             break;
323         case TelephonyManager.NETWORK_TYPE_UMTS:
324             networkTypeStr = "umts";
325             break;
326         case TelephonyManager.NETWORK_TYPE_HSDPA:
327             networkTypeStr = "hsdpa";
328             break;
329         case TelephonyManager.NETWORK_TYPE_HSUPA:
330             networkTypeStr = "hsupa";
331             break;
332         case TelephonyManager.NETWORK_TYPE_HSPA:
333             networkTypeStr = "hspa";
334             break;
335         case TelephonyManager.NETWORK_TYPE_HSPAP:
336             networkTypeStr = "hspap";
337             break;
338         case TelephonyManager.NETWORK_TYPE_CDMA:
339             networkTypeStr = "cdma";
340             break;
341         case TelephonyManager.NETWORK_TYPE_1xRTT:
342             networkTypeStr = "1xrtt";
343             break;
344         case TelephonyManager.NETWORK_TYPE_EVDO_0:
345             networkTypeStr = "evdo";
346             break;
347         case TelephonyManager.NETWORK_TYPE_EVDO_A:
348             networkTypeStr = "evdo";
349             break;
350         case TelephonyManager.NETWORK_TYPE_EVDO_B:
351             networkTypeStr = "evdo";
352             break;
353         case TelephonyManager.NETWORK_TYPE_IDEN:
354             networkTypeStr = "iden";
355             break;
356         case TelephonyManager.NETWORK_TYPE_LTE:
357             networkTypeStr = "lte";
358             break;
359         case TelephonyManager.NETWORK_TYPE_EHRPD:
360             networkTypeStr = "ehrpd";
361             break;
362         default:
363             loge("unknown network type: " + tm.getNetworkType());
364         }
365         return "net.tcp.buffersize." + networkTypeStr;
366     }
367
368     /**
369      * Tear down mobile data connectivity, i.e., disable the ability to create
370      * mobile data connections.
371      * TODO - make async and return nothing?
372      */
373     public boolean teardown() {
374         setTeardownRequested(true);
375         return (setEnableApn(mApnType, false) != PhoneConstants.APN_REQUEST_FAILED);
376     }
377
378     @Override
379     public void captivePortalCheckComplete() {
380         // not implemented
381     }
382
383     @Override
384     public void captivePortalCheckCompleted(boolean isCaptivePortal) {
385         if (mIsCaptivePortal.getAndSet(isCaptivePortal) != isCaptivePortal) {
386             // Captive portal change enable/disable failing fast
387             setEnableFailFastMobileData(
388                     isCaptivePortal ? DctConstants.ENABLED : DctConstants.DISABLED);
389         }
390     }
391
392     /**
393      * Record the detailed state of a network, and if it is a
394      * change from the previous state, send a notification to
395      * any listeners.
396      * @param state the new {@code DetailedState}
397      * @param reason a {@code String} indicating a reason for the state change,
398      * if one was supplied. May be {@code null}.
399      * @param extraInfo optional {@code String} providing extra information about the state change
400      */
401     private void setDetailedState(NetworkInfo.DetailedState state, String reason,
402             String extraInfo) {
403         if (DBG) log("setDetailed state, old ="
404                 + mNetworkInfo.getDetailedState() + " and new state=" + state);
405         if (state != mNetworkInfo.getDetailedState()) {
406             boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
407             String lastReason = mNetworkInfo.getReason();
408             /*
409              * If a reason was supplied when the CONNECTING state was entered, and no
410              * reason was supplied for entering the CONNECTED state, then retain the
411              * reason that was supplied when going to CONNECTING.
412              */
413             if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
414                     && lastReason != null)
415                 reason = lastReason;
416             mNetworkInfo.setDetailedState(state, reason, extraInfo);
417             Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo));
418             msg.sendToTarget();
419         }
420     }
421
422     public void setTeardownRequested(boolean isRequested) {
423         mTeardownRequested = isRequested;
424     }
425
426     public boolean isTeardownRequested() {
427         return mTeardownRequested;
428     }
429
430     /**
431      * Re-enable mobile data connectivity after a {@link #teardown()}.
432      * TODO - make async and always get a notification?
433      */
434     public boolean reconnect() {
435         boolean retValue = false; //connected or expect to be?
436         setTeardownRequested(false);
437         switch (setEnableApn(mApnType, true)) {
438             case PhoneConstants.APN_ALREADY_ACTIVE:
439                 // need to set self to CONNECTING so the below message is handled.
440                 retValue = true;
441                 break;
442             case PhoneConstants.APN_REQUEST_STARTED:
443                 // set IDLE here , avoid the following second FAILED not sent out
444                 mNetworkInfo.setDetailedState(DetailedState.IDLE, null, null);
445                 retValue = true;
446                 break;
447             case PhoneConstants.APN_REQUEST_FAILED:
448             case PhoneConstants.APN_TYPE_NOT_AVAILABLE:
449                 break;
450             default:
451                 loge("Error in reconnect - unexpected response.");
452                 break;
453         }
454         return retValue;
455     }
456
457     /**
458      * Turn on or off the mobile radio. No connectivity will be possible while the
459      * radio is off. The operation is a no-op if the radio is already in the desired state.
460      * @param turnOn {@code true} if the radio should be turned on, {@code false} if
461      */
462     public boolean setRadio(boolean turnOn) {
463         getPhoneService(false);
464         /*
465          * If the phone process has crashed in the past, we'll get a
466          * RemoteException and need to re-reference the service.
467          */
468         for (int retry = 0; retry < 2; retry++) {
469             if (mPhoneService == null) {
470                 loge("Ignoring mobile radio request because could not acquire PhoneService");
471                 break;
472             }
473
474             try {
475                 return mPhoneService.setRadio(turnOn);
476             } catch (RemoteException e) {
477                 if (retry == 0) getPhoneService(true);
478             }
479         }
480
481         loge("Could not set radio power to " + (turnOn ? "on" : "off"));
482         return false;
483     }
484
485     @Override
486     public void setUserDataEnable(boolean enabled) {
487         if (DBG) log("setUserDataEnable: E enabled=" + enabled);
488         final AsyncChannel channel = mDataConnectionTrackerAc;
489         if (channel != null) {
490             channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE,
491                     enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
492             mUserDataEnabled = enabled;
493         }
494         if (VDBG) log("setUserDataEnable: X enabled=" + enabled);
495     }
496
497     @Override
498     public void setPolicyDataEnable(boolean enabled) {
499         if (DBG) log("setPolicyDataEnable(enabled=" + enabled + ")");
500         final AsyncChannel channel = mDataConnectionTrackerAc;
501         if (channel != null) {
502             channel.sendMessage(DctConstants.CMD_SET_POLICY_DATA_ENABLE,
503                     enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
504             mPolicyDataEnabled = enabled;
505         }
506     }
507
508     /**
509      * Eanble/disable FailFast
510      *
511      * @param enabled is DctConstants.ENABLED/DISABLED
512      */
513     public void setEnableFailFastMobileData(int enabled) {
514         if (DBG) log("setEnableFailFastMobileData(enabled=" + enabled + ")");
515         final AsyncChannel channel = mDataConnectionTrackerAc;
516         if (channel != null) {
517             channel.sendMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled);
518         }
519     }
520
521     /**
522      * carrier dependency is met/unmet
523      * @param met
524      */
525     public void setDependencyMet(boolean met) {
526         Bundle bundle = Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType);
527         try {
528             if (DBG) log("setDependencyMet: E met=" + met);
529             Message msg = Message.obtain();
530             msg.what = DctConstants.CMD_SET_DEPENDENCY_MET;
531             msg.arg1 = (met ? DctConstants.ENABLED : DctConstants.DISABLED);
532             msg.setData(bundle);
533             mDataConnectionTrackerAc.sendMessage(msg);
534             if (VDBG) log("setDependencyMet: X met=" + met);
535         } catch (NullPointerException e) {
536             loge("setDependencyMet: X mAc was null" + e);
537         }
538     }
539
540     @Override
541     public void addStackedLink(LinkProperties link) {
542         mLinkProperties.addStackedLink(link);
543     }
544
545     @Override
546     public void removeStackedLink(LinkProperties link) {
547         mLinkProperties.removeStackedLink(link);
548     }
549
550     @Override
551     public String toString() {
552         final CharArrayWriter writer = new CharArrayWriter();
553         final PrintWriter pw = new PrintWriter(writer);
554         pw.print("Mobile data state: "); pw.println(mMobileDataState);
555         pw.print("Data enabled: user="); pw.print(mUserDataEnabled);
556         pw.print(", policy="); pw.println(mPolicyDataEnabled);
557         return writer.toString();
558     }
559
560    /**
561      * Internal method supporting the ENABLE_MMS feature.
562      * @param apnType the type of APN to be enabled or disabled (e.g., mms)
563      * @param enable {@code true} to enable the specified APN type,
564      * {@code false} to disable it.
565      * @return an integer value representing the outcome of the request.
566      */
567     private int setEnableApn(String apnType, boolean enable) {
568         getPhoneService(false);
569         /*
570          * If the phone process has crashed in the past, we'll get a
571          * RemoteException and need to re-reference the service.
572          */
573         for (int retry = 0; retry < 2; retry++) {
574             if (mPhoneService == null) {
575                 loge("Ignoring feature request because could not acquire PhoneService");
576                 break;
577             }
578
579             try {
580                 if (enable) {
581                     return mPhoneService.enableApnType(apnType);
582                 } else {
583                     return mPhoneService.disableApnType(apnType);
584                 }
585             } catch (RemoteException e) {
586                 if (retry == 0) getPhoneService(true);
587             }
588         }
589
590         loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\"");
591         return PhoneConstants.APN_REQUEST_FAILED;
592     }
593
594     public static String networkTypeToApnType(int netType) {
595         switch(netType) {
596             case ConnectivityManager.TYPE_MOBILE:
597                 return PhoneConstants.APN_TYPE_DEFAULT;  // TODO - use just one of these
598             case ConnectivityManager.TYPE_MOBILE_MMS:
599                 return PhoneConstants.APN_TYPE_MMS;
600             case ConnectivityManager.TYPE_MOBILE_SUPL:
601                 return PhoneConstants.APN_TYPE_SUPL;
602             case ConnectivityManager.TYPE_MOBILE_DUN:
603                 return PhoneConstants.APN_TYPE_DUN;
604             case ConnectivityManager.TYPE_MOBILE_HIPRI:
605                 return PhoneConstants.APN_TYPE_HIPRI;
606             case ConnectivityManager.TYPE_MOBILE_FOTA:
607                 return PhoneConstants.APN_TYPE_FOTA;
608             case ConnectivityManager.TYPE_MOBILE_IMS:
609                 return PhoneConstants.APN_TYPE_IMS;
610             case ConnectivityManager.TYPE_MOBILE_CBS:
611                 return PhoneConstants.APN_TYPE_CBS;
612             default:
613                 sloge("Error mapping networkType " + netType + " to apnType.");
614                 return null;
615         }
616     }
617
618     /**
619      * @see android.net.NetworkStateTracker#getLinkProperties()
620      */
621     public LinkProperties getLinkProperties() {
622         return new LinkProperties(mLinkProperties);
623     }
624
625     /**
626      * @see android.net.NetworkStateTracker#getLinkCapabilities()
627      */
628     public LinkCapabilities getLinkCapabilities() {
629         return new LinkCapabilities(mLinkCapabilities);
630     }
631
632     public void supplyMessenger(Messenger messenger) {
633         if (VDBG) log(mApnType + " got supplyMessenger");
634         AsyncChannel ac = new AsyncChannel();
635         ac.connect(mContext, MobileDataStateTracker.this.mHandler, messenger);
636     }
637
638     private void log(String s) {
639         Slog.d(TAG, mApnType + ": " + s);
640     }
641
642     private void loge(String s) {
643         Slog.e(TAG, mApnType + ": " + s);
644     }
645
646     static private void sloge(String s) {
647         Slog.e(TAG, s);
648     }
649 }