summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWink Saville <wink@google.com>2013-06-29 21:10:57 -0700
committerWink Saville <wink@google.com>2013-06-29 21:10:57 -0700
commitab9321d13de29cf314e8a1774b592d1f1d7e94d8 (patch)
tree3ddf662a65410572a2d0634252212a383e7f840f
parent99e4a0f576bad57782b31f1b3329a3d2408f42ae (diff)
downloadframeworks_base-ab9321d13de29cf314e8a1774b592d1f1d7e94d8.zip
frameworks_base-ab9321d13de29cf314e8a1774b592d1f1d7e94d8.tar.gz
frameworks_base-ab9321d13de29cf314e8a1774b592d1f1d7e94d8.tar.bz2
Add checkMobileProvisioning to ConnectivityService.
Bug: 9279964 Change-Id: I42c326a21e05aa301e9d974ed9ac1d59472780ec
-rw-r--r--core/java/android/net/ConnectivityManager.java64
-rw-r--r--core/java/android/net/IConnectivityManager.aidl3
-rw-r--r--core/java/android/net/MobileDataStateTracker.java13
-rw-r--r--core/res/res/values/config.xml9
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--services/java/com/android/server/ConnectivityService.java512
-rw-r--r--telephony/java/com/android/internal/telephony/DctConstants.java1
7 files changed, 601 insertions, 3 deletions
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 78bf9af..6487c92 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -25,6 +25,7 @@ import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Messenger;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.provider.Settings;
import java.net.InetAddress;
@@ -1294,4 +1295,67 @@ public class ConnectivityManager {
} catch (RemoteException e) {
}
}
+
+ /**
+ * The ResultReceiver resultCode for checkMobileProvisioning (CMP_RESULT_CODE)
+ */
+
+ /**
+ * No connection was possible to the network.
+ * {@hide}
+ */
+ public static final int CMP_RESULT_CODE_NO_CONNECTION = 0;
+
+ /**
+ * A connection was made to the internet, all is well.
+ * {@hide}
+ */
+ public static final int CMP_RESULT_CODE_CONNECTABLE = 1;
+
+ /**
+ * A connection was made but there was a redirection, we appear to be in walled garden.
+ * This is an indication of a warm sim on a mobile network.
+ * {@hide}
+ */
+ public static final int CMP_RESULT_CODE_REDIRECTED = 2;
+
+ /**
+ * A connection was made but no dns server was available to resolve a name to address.
+ * This is an indication of a warm sim on a mobile network.
+ *
+ * {@hide}
+ */
+ public static final int CMP_RESULT_CODE_NO_DNS = 3;
+
+ /**
+ * A connection was made but could not open a TCP connection.
+ * This is an indication of a warm sim on a mobile network.
+ * {@hide}
+ */
+ public static final int CMP_RESULT_CODE_NO_TCP_CONNECTION = 4;
+
+ /**
+ * Check mobile provisioning. The resultCode passed to
+ * onReceiveResult will be one of the CMP_RESULT_CODE_xxxx values above.
+ * This may take a minute or more to complete.
+ *
+ * @param sendNotificaiton, when true a notification will be sent to user.
+ * @param suggestedTimeOutMs, timeout in milliseconds
+ * @param resultReceiver needs to be supplied to receive the result
+ *
+ * @return time out that will be used, maybe less that suggestedTimeOutMs
+ * -1 if an error.
+ *
+ * {@hide}
+ */
+ public int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs,
+ ResultReceiver resultReceiver) {
+ int timeOutMs = -1;
+ try {
+ timeOutMs = mService.checkMobileProvisioning(sendNotification, suggestedTimeOutMs,
+ resultReceiver);
+ } catch (RemoteException e) {
+ }
+ return timeOutMs;
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index e5d6e51..3dbe078 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -24,6 +24,7 @@ import android.net.ProxyProperties;
import android.os.IBinder;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
+import android.os.ResultReceiver;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
@@ -131,4 +132,6 @@ interface IConnectivityManager
void supplyMessenger(int networkType, in Messenger messenger);
int findConnectionTypeForIface(in String iface);
+
+ int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs, in ResultReceiver resultReceiver);
}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index e85dbcd..5a1daed 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -494,6 +494,19 @@ public class MobileDataStateTracker implements NetworkStateTracker {
}
/**
+ * Eanble/disable FailFast
+ *
+ * @param enabled is DctConstants.ENABLED/DISABLED
+ */
+ public void setEnableFailFastMobileData(int enabled) {
+ if (DBG) log("setEnableFailFastMobileData(enabled=" + enabled + ")");
+ final AsyncChannel channel = mDataConnectionTrackerAc;
+ if (channel != null) {
+ channel.sendMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled);
+ }
+ }
+
+ /**
* carrier dependency is met/unmet
* @param met
*/
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4bd1c9b..4572f5b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -772,6 +772,15 @@
<!-- IP address of the dns server to use if nobody else suggests one -->
<string name="config_default_dns_server" translatable="false">8.8.8.8</string>
+ <!-- The default mobile provisioning url. Empty by default, maybe overridden by
+ an mcc/mnc specific config.xml -->
+ <string name="mobile_provisioning_url" translatable="false"></string>
+
+ <!-- This url is used as the default url when redirection is detected. Any
+ should work as all url's get redirected. But maybe overridden by
+ if needed. -->
+ <string name="mobile_redirected_provisioning_url" translatable="false">http://google.com</string>
+
<!-- The default character set for GsmAlphabet -->
<!-- Empty string means MBCS is not considered -->
<string name="gsm_alphabet_default_charset" translatable="false"></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3ecdc0c..b885d56 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -703,6 +703,8 @@
<java-symbol type="string" name="preposition_for_time" />
<java-symbol type="string" name="progress_erasing" />
<java-symbol type="string" name="progress_unmounting" />
+ <java-symbol type="string" name="mobile_provisioning_url" />
+ <java-symbol type="string" name="mobile_redirected_provisioning_url" />
<java-symbol type="string" name="reboot_safemode_confirm" />
<java-symbol type="string" name="reboot_safemode_title" />
<java-symbol type="string" name="relationTypeAssistant" />
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 37a8cb8..9e9253a 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -31,6 +31,9 @@ import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -52,11 +55,13 @@ import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.Uri;
import android.net.LinkProperties.CompareResult;
import android.net.MobileDataStateTracker;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
import android.net.NetworkQuotaInfo;
import android.net.NetworkState;
import android.net.NetworkStateTracker;
@@ -66,6 +71,7 @@ import android.net.ProxyProperties;
import android.net.RouteInfo;
import android.net.wifi.WifiStateTracker;
import android.net.wimax.WimaxManagerConstants;
+import android.os.AsyncTask;
import android.os.Binder;
import android.os.FileUtils;
import android.os.Handler;
@@ -79,6 +85,7 @@ import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -86,13 +93,16 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseIntArray;
+import com.android.internal.R;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
+import com.android.internal.telephony.DctConstants;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.IndentingPrintWriter;
@@ -111,9 +121,11 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
+import java.net.HttpURLConnection;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -121,6 +133,8 @@ import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* @hide
@@ -141,6 +155,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
"android.telephony.apn-restore";
+ // Default value if FAIL_FAST_TIME_MS is not set
+ private static final int DEFAULT_FAIL_FAST_TIME_MS = 1 * 60 * 1000;
+ // system property that can override DEFAULT_FAIL_FAST_TIME_MS
+ private static final String FAIL_FAST_TIME_MS =
+ "persist.radio.fail_fast_time_ms";
+
// used in recursive route setting to add gateways for the host for which
// a host route was requested.
private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10;
@@ -292,6 +312,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final int EVENT_VPN_STATE_CHANGED = 14;
+ /**
+ * Used internally to disable fail fast of mobile data
+ */
+ private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 15;
+
/** Handler used for internal events. */
private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
@@ -346,6 +371,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// the set of network types that can only be enabled by system/sig apps
List mProtectedNetworks;
+ private AtomicInteger mEnableFailFastMobileDataTag = new AtomicInteger(0);
+
+ TelephonyManager mTelephonyManager;
+
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
// Currently, omitting a NetworkFactory will create one internally
@@ -394,6 +423,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
mKeyStore = KeyStore.getInstance();
+ mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
try {
mPolicyManager.registerListener(mPolicyListener);
@@ -1408,8 +1438,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
netState != DetailedState.CAPTIVE_PORTAL_CHECK) ||
tracker.isTeardownRequested()) {
if (VDBG) {
- log("requestRouteToHostAddress on down network " +
- "(" + networkType + ") - dropped");
+ log("requestRouteToHostAddress on down network "
+ + "(" + networkType + ") - dropped"
+ + " tracker=" + tracker
+ + " netState=" + netState
+ + " isTeardownRequested="
+ + ((tracker != null) ? tracker.isTeardownRequested() : "tracker:null"));
}
return false;
}
@@ -1417,12 +1451,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
try {
InetAddress addr = InetAddress.getByAddress(hostAddress);
LinkProperties lp = tracker.getLinkProperties();
- return addRouteToAddress(lp, addr);
+ boolean ok = addRouteToAddress(lp, addr);
+ if (DBG) log("requestRouteToHostAddress ok=" + ok);
+ return ok;
} catch (UnknownHostException e) {
if (DBG) log("requestRouteToHostAddress got " + e.toString());
} finally {
Binder.restoreCallingIdentity(token);
}
+ if (DBG) log("requestRouteToHostAddress X bottom return false");
return false;
}
@@ -2824,6 +2861,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
break;
}
+ case EVENT_ENABLE_FAIL_FAST_MOBILE_DATA: {
+ int tag = mEnableFailFastMobileDataTag.get();
+ if (msg.arg1 == tag) {
+ MobileDataStateTracker mobileDst =
+ (MobileDataStateTracker) mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+ if (mobileDst != null) {
+ mobileDst.setEnableFailFastMobileData(msg.arg2);
+ }
+ } else {
+ log("EVENT_ENABLE_FAIL_FAST_MOBILE_DATA: stale arg1:" + msg.arg1
+ + " != tag:" + tag);
+ }
+ }
}
}
}
@@ -3472,4 +3522,460 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
return ConnectivityManager.TYPE_NONE;
}
+
+ /**
+ * Have mobile data fail fast if enabled.
+ *
+ * @param enabled DctConstants.ENABLED/DISABLED
+ */
+ private void setEnableFailFastMobileData(int enabled) {
+ int tag;
+
+ if (enabled == DctConstants.ENABLED) {
+ tag = mEnableFailFastMobileDataTag.incrementAndGet();
+ } else {
+ tag = mEnableFailFastMobileDataTag.get();
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_ENABLE_FAIL_FAST_MOBILE_DATA, tag,
+ enabled));
+ }
+
+ @Override
+ public int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs,
+ final ResultReceiver resultReceiver) {
+ log("checkMobileProvisioning: E sendNotification=" + sendNotification
+ + " suggestedTimeOutMs=" + suggestedTimeOutMs
+ + " resultReceiver=" + resultReceiver);
+ enforceChangePermission();
+
+ int timeOutMs = suggestedTimeOutMs;
+ if (suggestedTimeOutMs > CheckMp.MAX_TIMEOUT_MS) {
+ timeOutMs = CheckMp.MAX_TIMEOUT_MS;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ CheckMp checkMp = new CheckMp(mContext, this);
+ CheckMp.CallBack cb = new CheckMp.CallBack() {
+ @Override
+ void onComplete(Integer result) {
+ log("CheckMp.onComplete: result=" + result);
+ if (resultReceiver != null) {
+ log("CheckMp.onComplete: send result");
+ resultReceiver.send(result, null);
+ }
+ NetworkInfo ni =
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI].getNetworkInfo();
+ switch(result) {
+ case ConnectivityManager.CMP_RESULT_CODE_CONNECTABLE:
+ case ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION: {
+ log("CheckMp.onComplete: ignore, connected or no connection");
+ break;
+ }
+ case ConnectivityManager.CMP_RESULT_CODE_REDIRECTED: {
+ log("CheckMp.onComplete: warm sim");
+ String url = getProvisioningUrl();
+ if (TextUtils.isEmpty(url)) {
+ url = mContext.getResources()
+ .getString(R.string.mobile_redirected_provisioning_url);
+ }
+ if (TextUtils.isEmpty(url) == false) {
+ log("CheckMp.onComplete: warm sim (redirected), url=" + url);
+ setNotificationVisible(true, ni, url);
+ } else {
+ log("CheckMp.onComplete: warm sim (redirected), no url");
+ }
+ break;
+ }
+ case ConnectivityManager.CMP_RESULT_CODE_NO_DNS:
+ case ConnectivityManager.CMP_RESULT_CODE_NO_TCP_CONNECTION: {
+ String url = getProvisioningUrl();
+ if (TextUtils.isEmpty(url) == false) {
+ log("CheckMp.onComplete: warm sim (no dns/tcp), url=" + url);
+ setNotificationVisible(true, ni, url);
+ } else {
+ log("CheckMp.onComplete: warm sim (no dns/tcp), no url");
+ }
+ break;
+ }
+ default: {
+ loge("CheckMp.onComplete: ignore unexpected result=" + result);
+ break;
+ }
+ }
+ }
+ };
+ CheckMp.Params params =
+ new CheckMp.Params(checkMp.getDefaultUrl(), timeOutMs, cb);
+ log("checkMobileProvisioning: params=" + params);
+ setNotificationVisible(false, null, null);
+ checkMp.execute(params);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ log("checkMobileProvisioning: X");
+ }
+ return timeOutMs;
+ }
+
+ static class CheckMp extends
+ AsyncTask<CheckMp.Params, Void, Integer> {
+ private static final String CHECKMP_TAG = "CheckMp";
+ public static final int MAX_TIMEOUT_MS = 60000;
+ private static final int SOCKET_TIMEOUT_MS = 5000;
+ private Context mContext;
+ private ConnectivityService mCs;
+ private TelephonyManager mTm;
+ private Params mParams;
+
+ /**
+ * Parameters for AsyncTask.execute
+ */
+ static class Params {
+ private String mUrl;
+ private long mTimeOutMs;
+ private CallBack mCb;
+
+ Params(String url, long timeOutMs, CallBack cb) {
+ mUrl = url;
+ mTimeOutMs = timeOutMs;
+ mCb = cb;
+ }
+
+ @Override
+ public String toString() {
+ return "{" + " url=" + mUrl + " mTimeOutMs=" + mTimeOutMs + " mCb=" + mCb + "}";
+ }
+ }
+
+ /**
+ * The call back object passed in Params. onComplete will be called
+ * on the main thread.
+ */
+ abstract static class CallBack {
+ // Called on the main thread.
+ abstract void onComplete(Integer result);
+ }
+
+ public CheckMp(Context context, ConnectivityService cs) {
+ mContext = context;
+ mCs = cs;
+
+ // Setup access to TelephonyService we'll be using.
+ mTm = (TelephonyManager) mContext.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ }
+
+ /**
+ * Get the default url to use for the test.
+ */
+ public String getDefaultUrl() {
+ // See http://go/clientsdns for usage approval
+ String server = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.CAPTIVE_PORTAL_SERVER);
+ if (server == null) {
+ server = "clients3.google.com";
+ }
+ return "http://" + server + "/generate_204";
+ }
+
+ /**
+ * Detect if its possible to connect to the http url. DNS based detection techniques
+ * do not work at all hotspots. The best way to check is to perform a request to
+ * a known address that fetches the data we expect.
+ */
+ private synchronized Integer isMobileOk(Params params) {
+ Integer result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+ Uri orgUri = Uri.parse(params.mUrl);
+ Random rand = new Random();
+ mParams = params;
+
+ try {
+ if (mCs.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
+ log("isMobileOk: not mobile capable");
+ result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+ return result;
+ }
+
+ // Enable fail fast as we'll do retries here and use a
+ // hipri connection so the default connection stays active.
+ log("isMobileOk: start hipri url=" + params.mUrl);
+ mCs.setEnableFailFastMobileData(DctConstants.ENABLED);
+ mCs.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+ Phone.FEATURE_ENABLE_HIPRI, new Binder());
+
+ // Continue trying to connect until time has run out
+ long endTime = SystemClock.elapsedRealtime() + params.mTimeOutMs;
+ while(SystemClock.elapsedRealtime() < endTime) {
+ try {
+ // Wait for hipri to connect.
+ // TODO: Don't poll and handle situation where hipri fails
+ // because default is retrying. See b/9569540
+ NetworkInfo.State state = mCs
+ .getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
+ if (state != NetworkInfo.State.CONNECTED) {
+ log("isMobileOk: not connected ni=" +
+ mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
+ sleep(1);
+ result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+ continue;
+ }
+
+ // Get of the addresses associated with the url host. We need to use the
+ // address otherwise HttpURLConnection object will use the name to get
+ // the addresses and is will try every address but that will bypass the
+ // route to host we setup and the connection could succeed as the default
+ // interface might be connected to the internet via wifi or other interface.
+ InetAddress[] addresses;
+ try {
+ addresses = InetAddress.getAllByName(orgUri.getHost());
+ } catch (UnknownHostException e) {
+ log("isMobileOk: UnknownHostException");
+ result = ConnectivityManager.CMP_RESULT_CODE_NO_DNS;
+ return result;
+ }
+ log("isMobileOk: addresses=" + inetAddressesToString(addresses));
+
+ // Get the type of addresses supported by this link
+ LinkProperties lp = mCs.getLinkProperties(
+ ConnectivityManager.TYPE_MOBILE_HIPRI);
+ boolean linkHasIpv4 = hasIPv4Address(lp);
+ boolean linkHasIpv6 = hasIPv6Address(lp);
+ log("isMobileOk: linkHasIpv4=" + linkHasIpv4
+ + " linkHasIpv6=" + linkHasIpv6);
+
+ // Loop through at most 3 valid addresses or all of the address or until
+ // we run out of time
+ int loops = Math.min(3, addresses.length);
+ for(int validAddr=0, addrTried=0;
+ (validAddr < loops) && (addrTried < addresses.length)
+ && (SystemClock.elapsedRealtime() < endTime);
+ addrTried ++) {
+
+ // Choose the address at random but make sure its type is supported
+ InetAddress hostAddr = addresses[rand.nextInt(addresses.length)];
+ if (((hostAddr instanceof Inet4Address) && linkHasIpv4)
+ || ((hostAddr instanceof Inet6Address) && linkHasIpv6)) {
+ // Valid address, so use it
+ validAddr += 1;
+ } else {
+ // Invalid address so try next address
+ continue;
+ }
+
+ // Make a route to host so we check the specific interface.
+ if (mCs.requestRouteToHostAddress(ConnectivityManager.TYPE_MOBILE_HIPRI,
+ hostAddr.getAddress())) {
+ // Wait a short time to be sure the route is established ??
+ log("isMobileOk:"
+ + " wait to establish route to hostAddr=" + hostAddr);
+ sleep(3);
+ } else {
+ log("isMobileOk:"
+ + " could not establish route to hostAddr=" + hostAddr);
+ continue;
+ }
+
+ // Rewrite the url to have numeric address to use the specific route.
+ // I also set the "Connection" to "Close" as by default "Keep-Alive"
+ // is used which is useless in this case.
+ URL newUrl = new URL(orgUri.getScheme() + "://"
+ + hostAddr.getHostAddress() + orgUri.getPath());
+ log("isMobileOk: newUrl=" + newUrl);
+
+ HttpURLConnection urlConn = null;
+ try {
+ // Open the connection set the request header and get the response
+ urlConn = (HttpURLConnection) newUrl.openConnection(
+ java.net.Proxy.NO_PROXY);
+ urlConn.setInstanceFollowRedirects(false);
+ urlConn.setConnectTimeout(SOCKET_TIMEOUT_MS);
+ urlConn.setReadTimeout(SOCKET_TIMEOUT_MS);
+ urlConn.setUseCaches(false);
+ urlConn.setAllowUserInteraction(false);
+ urlConn.setRequestProperty("Connection", "close");
+ int responseCode = urlConn.getResponseCode();
+ if (responseCode == 204) {
+ result = ConnectivityManager.CMP_RESULT_CODE_CONNECTABLE;
+ } else {
+ result = ConnectivityManager.CMP_RESULT_CODE_REDIRECTED;
+ }
+ log("isMobileOk: connected responseCode=" + responseCode);
+ urlConn.disconnect();
+ urlConn = null;
+ return result;
+ } catch (Exception e) {
+ log("isMobileOk: HttpURLConnection Exception e=" + e);
+ if (urlConn != null) {
+ urlConn.disconnect();
+ urlConn = null;
+ }
+ }
+ }
+ result = ConnectivityManager.CMP_RESULT_CODE_NO_TCP_CONNECTION;
+ log("isMobileOk: loops|timed out");
+ return result;
+ } catch (Exception e) {
+ log("isMobileOk: Exception e=" + e);
+ continue;
+ }
+ }
+ log("isMobileOk: timed out");
+ } finally {
+ log("isMobileOk: F stop hipri");
+ mCs.setEnableFailFastMobileData(DctConstants.DISABLED);
+ mCs.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
+ Phone.FEATURE_ENABLE_HIPRI);
+ log("isMobileOk: X result=" + result);
+ }
+ return result;
+ }
+
+ @Override
+ protected Integer doInBackground(Params... params) {
+ return isMobileOk(params[0]);
+ }
+
+ @Override
+ protected void onPostExecute(Integer result) {
+ log("onPostExecute: result=" + result);
+ if ((mParams != null) && (mParams.mCb != null)) {
+ mParams.mCb.onComplete(result);
+ }
+ }
+
+ private String inetAddressesToString(InetAddress[] addresses) {
+ StringBuffer sb = new StringBuffer();
+ boolean firstTime = true;
+ for(InetAddress addr : addresses) {
+ if (firstTime) {
+ firstTime = false;
+ } else {
+ sb.append(",");
+ }
+ sb.append(addr);
+ }
+ return sb.toString();
+ }
+
+ private void printNetworkInfo() {
+ boolean hasIccCard = mTm.hasIccCard();
+ int simState = mTm.getSimState();
+ log("hasIccCard=" + hasIccCard
+ + " simState=" + simState);
+ NetworkInfo[] ni = mCs.getAllNetworkInfo();
+ if (ni != null) {
+ log("ni.length=" + ni.length);
+ for (NetworkInfo netInfo: ni) {
+ log("netInfo=" + netInfo.toString());
+ }
+ } else {
+ log("no network info ni=null");
+ }
+ }
+
+ /**
+ * Sleep for a few seconds then return.
+ * @param seconds
+ */
+ private static void sleep(int seconds) {
+ try {
+ Thread.sleep(seconds * 1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean hasIPv4Address(LinkProperties lp) {
+ return lp.hasIPv4Address();
+ }
+
+ // Not implemented in LinkProperties, do it here.
+ public boolean hasIPv6Address(LinkProperties lp) {
+ for (LinkAddress address : lp.getLinkAddresses()) {
+ if (address.getAddress() instanceof Inet6Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void log(String s) {
+ Slog.d(ConnectivityService.TAG, "[" + CHECKMP_TAG + "] " + s);
+ }
+ }
+
+ private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
+
+ private void setNotificationVisible(boolean visible, NetworkInfo networkInfo, String url) {
+ log("setNotificationVisible: E visible=" + visible + " ni=" + networkInfo + " url=" + url);
+
+ Resources r = Resources.getSystem();
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+
+ if (visible) {
+ CharSequence title;
+ CharSequence details;
+ int icon;
+ switch (networkInfo.getType()) {
+ case ConnectivityManager.TYPE_WIFI:
+ log("setNotificationVisible: TYPE_WIFI");
+ title = r.getString(R.string.wifi_available_sign_in, 0);
+ details = r.getString(R.string.network_available_sign_in_detailed,
+ networkInfo.getExtraInfo());
+ icon = R.drawable.stat_notify_wifi_in_range;
+ break;
+ case ConnectivityManager.TYPE_MOBILE:
+ case ConnectivityManager.TYPE_MOBILE_HIPRI:
+ log("setNotificationVisible: TYPE_MOBILE|HIPRI");
+ title = r.getString(R.string.network_available_sign_in, 0);
+ // TODO: Change this to pull from NetworkInfo once a printable
+ // name has been added to it
+ details = mTelephonyManager.getNetworkOperatorName();
+ icon = R.drawable.stat_notify_rssi_in_range;
+ break;
+ default:
+ log("setNotificationVisible: other type=" + networkInfo.getType());
+ title = r.getString(R.string.network_available_sign_in, 0);
+ details = r.getString(R.string.network_available_sign_in_detailed,
+ networkInfo.getExtraInfo());
+ icon = R.drawable.stat_notify_rssi_in_range;
+ break;
+ }
+
+ Notification notification = new Notification();
+ notification.when = 0;
+ notification.icon = icon;
+ notification.flags = Notification.FLAG_AUTO_CANCEL;
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ notification.tickerText = title;
+ notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
+
+ log("setNotificaitionVisible: notify notificaiton=" + notification);
+ notificationManager.notify(NOTIFICATION_ID, 1, notification);
+ } else {
+ log("setNotificaitionVisible: cancel");
+ notificationManager.cancel(NOTIFICATION_ID, 1);
+ }
+ log("setNotificationVisible: X visible=" + visible + " ni=" + networkInfo + " url=" + url);
+ }
+
+ private String getProvisioningUrl() {
+ String url = mContext.getResources().getString(R.string.mobile_provisioning_url);
+ log("getProvisioningUrl: resource url=" + url);
+
+ // populate the iccid and imei in the provisioning url.
+ if (!TextUtils.isEmpty(url)) {
+ url = String.format(url,
+ mTelephonyManager.getSimSerialNumber() /* ICCID */,
+ mTelephonyManager.getDeviceId() /* IMEI */,
+ mTelephonyManager.getLine1Number() /* Phone numer */);
+ }
+
+ log("getProvisioningUrl: url=" + url);
+ return url;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 9d556c0..4d8342c 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -93,6 +93,7 @@ public class DctConstants {
public static final int EVENT_ICC_CHANGED = BASE + 33;
public static final int EVENT_DISCONNECT_DC_RETRYING = BASE + 34;
public static final int EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35;
+ public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 36;
/***** Constants *****/