Add checkMobileProvisioning to ConnectivityService.
Bug: 9279964 Change-Id: I42c326a21e05aa301e9d974ed9ac1d59472780ec
This commit is contained in:
@@ -25,6 +25,7 @@ import android.os.Binder;
|
|||||||
import android.os.Build.VERSION_CODES;
|
import android.os.Build.VERSION_CODES;
|
||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.ResultReceiver;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
@@ -1294,4 +1295,67 @@ public class ConnectivityManager {
|
|||||||
} catch (RemoteException e) {
|
} 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import android.net.ProxyProperties;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.ResultReceiver;
|
||||||
|
|
||||||
import com.android.internal.net.LegacyVpnInfo;
|
import com.android.internal.net.LegacyVpnInfo;
|
||||||
import com.android.internal.net.VpnConfig;
|
import com.android.internal.net.VpnConfig;
|
||||||
@@ -131,4 +132,6 @@ interface IConnectivityManager
|
|||||||
void supplyMessenger(int networkType, in Messenger messenger);
|
void supplyMessenger(int networkType, in Messenger messenger);
|
||||||
|
|
||||||
int findConnectionTypeForIface(in String iface);
|
int findConnectionTypeForIface(in String iface);
|
||||||
|
|
||||||
|
int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs, in ResultReceiver resultReceiver);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ import static android.net.ConnectivityManager.isNetworkTypeValid;
|
|||||||
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
|
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
|
||||||
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
|
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.bluetooth.BluetoothTetheringDataTracker;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
@@ -52,11 +55,13 @@ import android.net.INetworkPolicyManager;
|
|||||||
import android.net.INetworkStatsService;
|
import android.net.INetworkStatsService;
|
||||||
import android.net.LinkAddress;
|
import android.net.LinkAddress;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
|
import android.net.Uri;
|
||||||
import android.net.LinkProperties.CompareResult;
|
import android.net.LinkProperties.CompareResult;
|
||||||
import android.net.MobileDataStateTracker;
|
import android.net.MobileDataStateTracker;
|
||||||
import android.net.NetworkConfig;
|
import android.net.NetworkConfig;
|
||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.net.NetworkInfo.DetailedState;
|
import android.net.NetworkInfo.DetailedState;
|
||||||
|
import android.net.NetworkInfo.State;
|
||||||
import android.net.NetworkQuotaInfo;
|
import android.net.NetworkQuotaInfo;
|
||||||
import android.net.NetworkState;
|
import android.net.NetworkState;
|
||||||
import android.net.NetworkStateTracker;
|
import android.net.NetworkStateTracker;
|
||||||
@@ -66,6 +71,7 @@ import android.net.ProxyProperties;
|
|||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.net.wifi.WifiStateTracker;
|
import android.net.wifi.WifiStateTracker;
|
||||||
import android.net.wimax.WimaxManagerConstants;
|
import android.net.wimax.WimaxManagerConstants;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.FileUtils;
|
import android.os.FileUtils;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -79,6 +85,7 @@ import android.os.ParcelFileDescriptor;
|
|||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.ResultReceiver;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
@@ -86,13 +93,16 @@ import android.os.UserHandle;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.security.Credentials;
|
import android.security.Credentials;
|
||||||
import android.security.KeyStore;
|
import android.security.KeyStore;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
import com.android.internal.net.LegacyVpnInfo;
|
import com.android.internal.net.LegacyVpnInfo;
|
||||||
import com.android.internal.net.VpnConfig;
|
import com.android.internal.net.VpnConfig;
|
||||||
import com.android.internal.net.VpnProfile;
|
import com.android.internal.net.VpnProfile;
|
||||||
|
import com.android.internal.telephony.DctConstants;
|
||||||
import com.android.internal.telephony.Phone;
|
import com.android.internal.telephony.Phone;
|
||||||
import com.android.internal.telephony.PhoneConstants;
|
import com.android.internal.telephony.PhoneConstants;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
import com.android.internal.util.IndentingPrintWriter;
|
||||||
@@ -111,9 +121,11 @@ import java.io.FileDescriptor;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
import java.net.Inet4Address;
|
import java.net.Inet4Address;
|
||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.net.URL;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -121,6 +133,8 @@ import java.util.Collection;
|
|||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide
|
* @hide
|
||||||
@@ -141,6 +155,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
|
private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
|
||||||
"android.telephony.apn-restore";
|
"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
|
// used in recursive route setting to add gateways for the host for which
|
||||||
// a host route was requested.
|
// a host route was requested.
|
||||||
private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10;
|
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;
|
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. */
|
/** Handler used for internal events. */
|
||||||
private InternalHandler mHandler;
|
private InternalHandler mHandler;
|
||||||
/** Handler used for incoming {@link NetworkStateTracker} events. */
|
/** 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
|
// the set of network types that can only be enabled by system/sig apps
|
||||||
List mProtectedNetworks;
|
List mProtectedNetworks;
|
||||||
|
|
||||||
|
private AtomicInteger mEnableFailFastMobileDataTag = new AtomicInteger(0);
|
||||||
|
|
||||||
|
TelephonyManager mTelephonyManager;
|
||||||
|
|
||||||
public ConnectivityService(Context context, INetworkManagementService netd,
|
public ConnectivityService(Context context, INetworkManagementService netd,
|
||||||
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
|
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
|
||||||
// Currently, omitting a NetworkFactory will create one internally
|
// Currently, omitting a NetworkFactory will create one internally
|
||||||
@@ -394,6 +423,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
|
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
|
||||||
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
|
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
|
||||||
mKeyStore = KeyStore.getInstance();
|
mKeyStore = KeyStore.getInstance();
|
||||||
|
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mPolicyManager.registerListener(mPolicyListener);
|
mPolicyManager.registerListener(mPolicyListener);
|
||||||
@@ -1408,8 +1438,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
netState != DetailedState.CAPTIVE_PORTAL_CHECK) ||
|
netState != DetailedState.CAPTIVE_PORTAL_CHECK) ||
|
||||||
tracker.isTeardownRequested()) {
|
tracker.isTeardownRequested()) {
|
||||||
if (VDBG) {
|
if (VDBG) {
|
||||||
log("requestRouteToHostAddress on down network " +
|
log("requestRouteToHostAddress on down network "
|
||||||
"(" + networkType + ") - dropped");
|
+ "(" + networkType + ") - dropped"
|
||||||
|
+ " tracker=" + tracker
|
||||||
|
+ " netState=" + netState
|
||||||
|
+ " isTeardownRequested="
|
||||||
|
+ ((tracker != null) ? tracker.isTeardownRequested() : "tracker:null"));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1417,12 +1451,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
try {
|
try {
|
||||||
InetAddress addr = InetAddress.getByAddress(hostAddress);
|
InetAddress addr = InetAddress.getByAddress(hostAddress);
|
||||||
LinkProperties lp = tracker.getLinkProperties();
|
LinkProperties lp = tracker.getLinkProperties();
|
||||||
return addRouteToAddress(lp, addr);
|
boolean ok = addRouteToAddress(lp, addr);
|
||||||
|
if (DBG) log("requestRouteToHostAddress ok=" + ok);
|
||||||
|
return ok;
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
if (DBG) log("requestRouteToHostAddress got " + e.toString());
|
if (DBG) log("requestRouteToHostAddress got " + e.toString());
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(token);
|
Binder.restoreCallingIdentity(token);
|
||||||
}
|
}
|
||||||
|
if (DBG) log("requestRouteToHostAddress X bottom return false");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2824,6 +2861,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
}
|
}
|
||||||
break;
|
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;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user