private static final String TAG = "CaptivePortalTracker";
private static final String DEFAULT_SERVER = "clients3.google.com";
- private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
private static final int SOCKET_TIMEOUT_MS = 10000;
private String mServer;
private String mUrl;
- private boolean mNotificationShown = false;
private boolean mIsCaptivePortalCheckEnabled = false;
private IConnectivityManager mConnService;
private TelephonyManager mTelephonyManager;
private class DefaultState extends State {
@Override
public void enter() {
- if (DBG) log(getName() + "\n");
+ setNotificationOff();
}
@Override
public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
+ if (DBG) log(getName() + message.toString());
switch (message.what) {
case CMD_DETECT_PORTAL:
NetworkInfo info = (NetworkInfo) message.obj;
private class NoActiveNetworkState extends State {
@Override
public void enter() {
- if (DBG) log(getName() + "\n");
mNetworkInfo = null;
- /* Clear any previous notification */
- setNotificationVisible(false);
}
@Override
public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
+ if (DBG) log(getName() + message.toString());
InetAddress server;
NetworkInfo info;
switch (message.what) {
case CMD_CONNECTIVITY_CHANGE:
info = (NetworkInfo) message.obj;
- if (info.isConnected() && isActiveNetwork(info)) {
- mNetworkInfo = info;
- transitionTo(mDelayedCaptiveCheckState);
+ if (info.getType() == ConnectivityManager.TYPE_WIFI) {
+ if (info.isConnected() && isActiveNetwork(info)) {
+ mNetworkInfo = info;
+ transitionTo(mDelayedCaptiveCheckState);
+ }
+ } else {
+ log(getName() + " not a wifi connectivity change, ignore");
}
break;
default:
private class ActiveNetworkState extends State {
@Override
public void enter() {
- if (DBG) log(getName() + "\n");
+ setNotificationOff();
}
@Override
private class DelayedCaptiveCheckState extends State {
@Override
public void enter() {
- if (DBG) log(getName() + "\n");
Message message = obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, ++mDelayedCheckToken, 0);
if (mDeviceProvisioned) {
sendMessageDelayed(message, DELAYED_CHECK_INTERVAL_MS);
@Override
public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
+ if (DBG) log(getName() + message.toString());
switch (message.what) {
case CMD_DELAYED_CAPTIVE_CHECK:
if (message.arg1 == mDelayedCheckToken) {
if (captive) {
// Setup Wizard will assist the user in connecting to a captive
// portal, so make the notification visible unless during setup
- setNotificationVisible(true);
+ try {
+ mConnService.setProvisioningNotificationVisible(true,
+ mNetworkInfo.getType(), mNetworkInfo.getExtraInfo(), mUrl);
+ } catch(RemoteException e) {
+ e.printStackTrace();
+ }
}
} else {
Intent intent = new Intent(
return false;
}
+ private void setNotificationOff() {
+ try {
+ mConnService.setProvisioningNotificationVisible(false, ConnectivityManager.TYPE_NONE,
+ null, null);
+ } catch (RemoteException e) {
+ log("setNotificationOff: " + e);
+ }
+ }
+
/**
* Do a URL fetch on a known server to see if we get the data we expect
*/
urlConnection.getInputStream();
// we got a valid response, but not from the real google
return urlConnection.getResponseCode() != 204;
- } catch (SocketTimeoutException e) {
- if (DBG) log("Probably a portal: exception " + e);
- return true;
} catch (IOException e) {
if (DBG) log("Probably not a portal: exception " + e);
return false;
}
return null;
}
-
- private void setNotificationVisible(boolean visible) {
- // if it should be hidden and it is already hidden, then noop
- if (!visible && !mNotificationShown) {
- if (DBG) log("setNotivicationVisible: false and not shown, so noop");
- return;
- }
-
- Resources r = Resources.getSystem();
- NotificationManager notificationManager = (NotificationManager) mContext
- .getSystemService(Context.NOTIFICATION_SERVICE);
-
- if (visible) {
- CharSequence title;
- CharSequence details;
- int icon;
- String url = null;
- switch (mNetworkInfo.getType()) {
- case ConnectivityManager.TYPE_WIFI:
- title = r.getString(R.string.wifi_available_sign_in, 0);
- details = r.getString(R.string.network_available_sign_in_detailed,
- mNetworkInfo.getExtraInfo());
- icon = R.drawable.stat_notify_wifi_in_range;
- url = mUrl;
- break;
- case ConnectivityManager.TYPE_MOBILE:
- 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;
- try {
- url = mConnService.getMobileProvisioningUrl();
- if (TextUtils.isEmpty(url)) {
- url = mConnService.getMobileRedirectedProvisioningUrl();
- }
- } catch(RemoteException e) {
- e.printStackTrace();
- }
- if (TextUtils.isEmpty(url)) {
- url = mUrl;
- }
- break;
- default:
- title = r.getString(R.string.network_available_sign_in, 0);
- details = r.getString(R.string.network_available_sign_in_detailed,
- mNetworkInfo.getExtraInfo());
- icon = R.drawable.stat_notify_rssi_in_range;
- url = mUrl;
- 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);
-
- if (DBG) log("setNotivicationVisible: make visible");
- notificationManager.notify(NOTIFICATION_ID, 1, notification);
- } else {
- if (DBG) log("setNotivicationVisible: cancel notification");
- notificationManager.cancel(NOTIFICATION_ID, 1);
- }
- mNotificationShown = visible;
- }
}
public class MobileDataStateTracker implements NetworkStateTracker {
private static final String TAG = "MobileDataStateTracker";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final boolean VDBG = false;
private PhoneConstants.DataState mMobileDataState;
IntentFilter filter = new IntentFilter();
filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+ filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN);
filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
mContext.registerReceiver(new MobileDataStateReceiver(), filter);
public void releaseWakeLock() {
}
+ private void updateLinkProperitesAndCapatilities(Intent intent) {
+ mLinkProperties = intent.getParcelableExtra(
+ PhoneConstants.DATA_LINK_PROPERTIES_KEY);
+ if (mLinkProperties == null) {
+ loge("CONNECTED event did not supply link properties.");
+ mLinkProperties = new LinkProperties();
+ }
+ mLinkCapabilities = intent.getParcelableExtra(
+ PhoneConstants.DATA_LINK_CAPABILITIES_KEY);
+ if (mLinkCapabilities == null) {
+ loge("CONNECTED event did not supply link capabilities.");
+ mLinkCapabilities = new LinkCapabilities();
+ }
+ }
+
private class MobileDataStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(TelephonyIntents.
- ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
+ ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN)) {
+ String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
- if (VDBG) {
- log(String.format("Broadcast received: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED"
- + "mApnType=%s %s received apnType=%s", mApnType,
- TextUtils.equals(apnType, mApnType) ? "==" : "!=", apnType));
+ if (!TextUtils.equals(mApnType, apnType)) {
+ return;
+ }
+ if (DBG) {
+ log("Broadcast received: " + intent.getAction() + " apnType=" + apnType
+ + " apnName=" + apnName);
}
+
+ // Make us in the connecting state until we make a new TYPE_MOBILE_PROVISIONING
+ mMobileDataState = PhoneConstants.DataState.CONNECTING;
+ updateLinkProperitesAndCapatilities(intent);
+ setDetailedState(DetailedState.CONNECTED_TO_PROVISIONING_NETWORK, "", apnName);
+ } else if (intent.getAction().equals(TelephonyIntents.
+ ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
+ String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
if (!TextUtils.equals(apnType, mApnType)) {
return;
}
+ if (DBG) {
+ log("Broadcast received: " + intent.getAction() + " apnType=" + apnType);
+ }
int oldSubtype = mNetworkInfo.getSubtype();
int newSubType = TelephonyManager.getDefault().getNetworkType();
String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
mNetworkInfo.setRoaming(intent.getBooleanExtra(
PhoneConstants.DATA_NETWORK_ROAMING_KEY, false));
- if (VDBG) {
+ if (DBG) {
log(mApnType + " setting isAvailable to " +
intent.getBooleanExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY,false));
}
setDetailedState(DetailedState.SUSPENDED, reason, apnName);
break;
case CONNECTED:
- mLinkProperties = intent.getParcelableExtra(
- PhoneConstants.DATA_LINK_PROPERTIES_KEY);
- if (mLinkProperties == null) {
- loge("CONNECTED event did not supply link properties.");
- mLinkProperties = new LinkProperties();
- }
- mLinkCapabilities = intent.getParcelableExtra(
- PhoneConstants.DATA_LINK_CAPABILITIES_KEY);
- if (mLinkCapabilities == null) {
- loge("CONNECTED event did not supply link capabilities.");
- mLinkCapabilities = new LinkCapabilities();
- }
+ updateLinkProperitesAndCapatilities(intent);
setDetailedState(DetailedState.CONNECTED, reason, apnName);
break;
}
equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
if (!TextUtils.equals(apnType, mApnType)) {
- if (DBG) {
- log(String.format(
- "Broadcast received: ACTION_ANY_DATA_CONNECTION_FAILED ignore, " +
- "mApnType=%s != received apnType=%s", mApnType, apnType));
- }
return;
}
String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY);
String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
if (DBG) {
- log("Received " + intent.getAction() +
- " broadcast" + reason == null ? "" : "(" + reason + ")");
+ log("Broadcast received: " + intent.getAction() +
+ " reason=" + reason == null ? "null" : reason);
}
setDetailedState(DetailedState.FAILED, reason, apnName);
} else {
}
}
+ /**
+ * Inform DCT mobile provisioning has started, it ends when provisioning completes.
+ */
+ public void enableMobileProvisioning(String url) {
+ if (DBG) log("enableMobileProvisioning(url=" + url + ")");
+ final AsyncChannel channel = mDataConnectionTrackerAc;
+ if (channel != null) {
+ Message msg = Message.obtain();
+ msg.what = DctConstants.CMD_ENABLE_MOBILE_PROVISIONING;
+ msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, url));
+ channel.sendMessage(msg);
+ }
+ }
+
+ /**
+ * Return if this network is the provisioning network. Valid only if connected.
+ * @param met
+ */
+ public boolean isProvisioningNetwork() {
+ boolean retVal;
+ try {
+ Message msg = Message.obtain();
+ msg.what = DctConstants.CMD_IS_PROVISIONING_APN;
+ msg.setData(Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType));
+ Message result = mDataConnectionTrackerAc.sendMessageSynchronously(msg);
+ retVal = result.arg1 == DctConstants.ENABLED;
+ } catch (NullPointerException e) {
+ loge("isProvisioningNetwork: X " + e);
+ retVal = false;
+ }
+ if (DBG) log("isProvisioningNetwork: retVal=" + retVal);
+ return retVal;
+ }
+
@Override
public void addStackedLink(LinkProperties link) {
mLinkProperties.addStackedLink(link);
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.bluetooth.BluetoothTetheringDataTracker;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
TelephonyManager mTelephonyManager;
- // We only want one checkMobileProvisioning after booting.
- volatile boolean mFirstProvisioningCheckStarted = false;
-
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
// Currently, omitting a NetworkFactory will create one internally
mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
mSettingsObserver.observe(mContext);
- mCaptivePortalTracker = CaptivePortalTracker.makeCaptivePortalTracker(mContext, this);
- loadGlobalProxy();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
+ mContext.registerReceiver(mProvisioningReceiver, filter);
}
/**
return getNetworkInfo(mActiveDefaultNetwork, uid);
}
+ /**
+ * Find the first Provisioning network.
+ *
+ * @return NetworkInfo or null if none.
+ */
+ private NetworkInfo getProvisioningNetworkInfo() {
+ enforceAccessPermission();
+
+ // Find the first Provisioning Network
+ NetworkInfo provNi = null;
+ for (NetworkInfo ni : getAllNetworkInfo()) {
+ if (ni.getDetailedState()
+ == NetworkInfo.DetailedState.CONNECTED_TO_PROVISIONING_NETWORK) {
+ provNi = ni;
+ break;
+ }
+ }
+ if (DBG) log("getProvisioningNetworkInfo: X provNi=" + provNi);
+ return provNi;
+ }
+
+ /**
+ * Find the first Provisioning network or the ActiveDefaultNetwork
+ * if there is no Provisioning network
+ *
+ * @return NetworkInfo or null if none.
+ */
+ @Override
+ public NetworkInfo getProvisioningOrActiveNetworkInfo() {
+ enforceAccessPermission();
+
+ NetworkInfo provNi = getProvisioningNetworkInfo();
+ if (provNi == null) {
+ final int uid = Binder.getCallingUid();
+ provNi = getNetworkInfo(mActiveDefaultNetwork, uid);
+ }
+ if (DBG) log("getProvisioningOrActiveNetworkInfo: X provNi=" + provNi);
+ return provNi;
+ }
+
public NetworkInfo getActiveNetworkInfoUnfiltered() {
enforceAccessPermission();
if (isNetworkTypeValid(mActiveDefaultNetwork)) {
feature);
}
if (network.reconnect()) {
+ if (DBG) log("startUsingNetworkFeature X: return APN_REQUEST_STARTED");
return PhoneConstants.APN_REQUEST_STARTED;
} else {
+ if (DBG) log("startUsingNetworkFeature X: return APN_REQUEST_FAILED");
return PhoneConstants.APN_REQUEST_FAILED;
}
} else {
mNetRequestersPids[usedNetworkType].add(currentPid);
}
}
+ if (DBG) log("startUsingNetworkFeature X: return -1 unsupported feature.");
return -1;
}
}
+ if (DBG) log("startUsingNetworkFeature X: return APN_TYPE_NOT_AVAILABLE");
return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
} finally {
if (DBG) {
}
}
if (found && u != null) {
+ if (VDBG) log("stopUsingNetworkFeature: X");
// stop regardless of how many other time this proc had called start
return stopUsingNetworkFeature(u, true);
} else {
// none found!
- if (VDBG) log("stopUsingNetworkFeature - not a live request, ignoring");
+ if (VDBG) log("stopUsingNetworkFeature: X not a live request, ignoring");
return 1;
}
}
*/
if (mNetConfigs[prevNetType].isDefault()) {
if (mActiveDefaultNetwork == prevNetType) {
+ if (DBG) {
+ log("tryFailover: set mActiveDefaultNetwork=-1, prevNetType=" + prevNetType);
+ }
mActiveDefaultNetwork = -1;
}
}
void systemReady() {
+ mCaptivePortalTracker = CaptivePortalTracker.makeCaptivePortalTracker(mContext, this);
+ loadGlobalProxy();
+
synchronized(this) {
mSystemReady = true;
if (mInitialBroadcast != null) {
};
private boolean isNewNetTypePreferredOverCurrentNetType(int type) {
- if ((type != mNetworkPreference &&
- mNetConfigs[mActiveDefaultNetwork].priority >
- mNetConfigs[type].priority) ||
- mNetworkPreference == mActiveDefaultNetwork) return false;
+ if (((type != mNetworkPreference)
+ && (mNetConfigs[mActiveDefaultNetwork].priority > mNetConfigs[type].priority))
+ || (mNetworkPreference == mActiveDefaultNetwork)) {
+ return false;
+ }
return true;
}
final NetworkStateTracker thisNet = mNetTrackers[newNetType];
final String thisIface = thisNet.getLinkProperties().getInterfaceName();
+ if (VDBG) {
+ log("handleConnect: E newNetType=" + newNetType + " thisIface=" + thisIface
+ + " isFailover" + isFailover);
+ }
+
// if this is a default net and other default is running
// kill the one not preferred
if (mNetConfigs[newNetType].isDefault()) {
*/
private void handleConnectivityChange(int netType, boolean doReset) {
int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0;
+ if (VDBG) {
+ log("handleConnectivityChange: netType=" + netType + " doReset=" + doReset
+ + " resetMask=" + resetMask);
+ }
/*
* If a non-default network is enabled, add the host routes that
boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault());
if (resetMask != 0 || resetDns) {
+ if (VDBG) log("handleConnectivityChange: resetting");
if (curLp != null) {
+ if (VDBG) log("handleConnectivityChange: resetting curLp=" + curLp);
for (String iface : curLp.getAllInterfaceNames()) {
if (TextUtils.isEmpty(iface) == false) {
if (resetMask != 0) {
// Update 464xlat state.
NetworkStateTracker tracker = mNetTrackers[netType];
if (mClat.requiresClat(netType, tracker)) {
+
// If the connection was previously using clat, but is not using it now, stop the clat
// daemon. Normally, this happens automatically when the connection disconnects, but if
// the disconnect is not reported, or if the connection's LinkProperties changed for
for (RouteInfo r : routeDiff.removed) {
if (isLinkDefault || ! r.isDefaultRoute()) {
+ if (VDBG) log("updateRoutes: default remove route r=" + r);
removeRoute(curLp, r, TO_DEFAULT_TABLE);
}
if (isLinkDefault == false) {
// remove the default route unless somebody else has asked for it
String ifaceName = newLp.getInterfaceName();
if (TextUtils.isEmpty(ifaceName) == false && mAddedRoutes.contains(r) == false) {
- if (VDBG) log("Removing " + r + " for interface " + ifaceName);
try {
mNetd.removeRoute(ifaceName, r);
} catch (Exception e) {
public void handleMessage(Message msg) {
NetworkInfo info;
switch (msg.what) {
- case NetworkStateTracker.EVENT_STATE_CHANGED:
+ case NetworkStateTracker.EVENT_STATE_CHANGED: {
info = (NetworkInfo) msg.obj;
- int type = info.getType();
NetworkInfo.State state = info.getState();
if (VDBG || (state == NetworkInfo.State.CONNECTED) ||
state + "/" + info.getDetailedState());
}
- // After booting we'll check once for mobile provisioning
- // if we've provisioned by and connected.
- if (!mFirstProvisioningCheckStarted
+ // Since mobile has the notion of a network/apn that can be used for
+ // provisioning we need to check every time we're connected as
+ // CaptiveProtalTracker won't detected it because DCT doesn't report it
+ // as connected as ACTION_ANY_DATA_CONNECTION_STATE_CHANGED instead its
+ // reported as ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN. Which
+ // is received by MDST and sent here as EVENT_STATE_CHANGED.
+ if (ConnectivityManager.isNetworkTypeMobile(info.getType())
&& (0 != Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0))
&& (state == NetworkInfo.State.CONNECTED)) {
- log("check provisioning after booting");
- mFirstProvisioningCheckStarted = true;
- checkMobileProvisioning(true, CheckMp.MAX_TIMEOUT_MS, null);
+ checkMobileProvisioning(CheckMp.MAX_TIMEOUT_MS);
}
EventLogTags.writeConnectivityStateChanged(
} else if (info.getDetailedState() ==
DetailedState.CAPTIVE_PORTAL_CHECK) {
handleCaptivePortalTrackerCheck(info);
+ } else if (info.getDetailedState() ==
+ DetailedState.CONNECTED_TO_PROVISIONING_NETWORK) {
+ /**
+ * TODO: Create ConnectivityManager.TYPE_MOBILE_PROVISIONING
+ * for now its an in between network, its a network that
+ * is actually a default network but we don't want it to be
+ * announced as such to keep background applications from
+ * trying to use it. It turns out that some still try so we
+ * take the additional step of clearing any default routes
+ * to the link that may have incorrectly setup by the lower
+ * levels.
+ */
+ LinkProperties lp = getLinkProperties(info.getType());
+ if (DBG) {
+ log("EVENT_STATE_CHANGED: connected to provisioning network, lp=" + lp);
+ }
+
+ // Clear any default routes setup by the radio so
+ // any activity by applications trying to use this
+ // connection will fail until the provisioning network
+ // is enabled.
+ for (RouteInfo r : lp.getRoutes()) {
+ removeRoute(lp, r, TO_DEFAULT_TABLE);
+ }
} else if (state == NetworkInfo.State.DISCONNECTED) {
handleDisconnect(info);
} else if (state == NetworkInfo.State.SUSPENDED) {
mLockdownTracker.onNetworkInfoChanged(info);
}
break;
- case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
+ }
+ case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: {
info = (NetworkInfo) msg.obj;
// TODO: Temporary allowing network configuration
// change not resetting sockets.
// @see bug/4455071
handleConnectivityChange(info.getType(), false);
break;
- case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
+ }
+ case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: {
info = (NetworkInfo) msg.obj;
- type = info.getType();
+ int type = info.getType();
updateNetworkSettings(mNetTrackers[type]);
break;
+ }
}
}
}
private boolean isMobileDataStateTrackerReady() {
MobileDataStateTracker mdst =
- (MobileDataStateTracker) mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+ (MobileDataStateTracker) mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI];
return (mdst != null) && (mdst.isReady());
}
- @Override
- public int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs,
- final ResultReceiver resultReceiver) {
- log("checkMobileProvisioning: E sendNotification=" + sendNotification
- + " suggestedTimeOutMs=" + suggestedTimeOutMs
- + " resultReceiver=" + resultReceiver);
- enforceChangePermission();
+ /**
+ * The ResultReceiver resultCode for checkMobileProvisioning (CMP_RESULT_CODE)
+ */
- mFirstProvisioningCheckStarted = true;
+ /**
+ * No connection was possible to the network.
+ */
+ public static final int CMP_RESULT_CODE_NO_CONNECTION = 0;
- int timeOutMs = suggestedTimeOutMs;
- if (suggestedTimeOutMs > CheckMp.MAX_TIMEOUT_MS) {
- timeOutMs = CheckMp.MAX_TIMEOUT_MS;
- }
+ /**
+ * A connection was made to the internet, all is well.
+ */
+ public static final int CMP_RESULT_CODE_CONNECTABLE = 1;
- // Check that mobile networks are supported
- if (!isNetworkSupported(ConnectivityManager.TYPE_MOBILE)
- || !isNetworkSupported(ConnectivityManager.TYPE_MOBILE_HIPRI)) {
- log("checkMobileProvisioning: X no mobile network");
- if (resultReceiver != null) {
- resultReceiver.send(ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION, null);
- }
- return timeOutMs;
- }
+ /**
+ * 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ public static final int CMP_RESULT_CODE_NO_TCP_CONNECTION = 4;
+
+ /**
+ * The mobile network is a provisioning network.
+ * This is an indication of a warm sim on a mobile network.
+ */
+ public static final int CMP_RESULT_CODE_PROVISIONING_NETWORK = 5;
+
+ AtomicBoolean mIsCheckingMobileProvisioning = new AtomicBoolean(false);
+
+ @Override
+ public int checkMobileProvisioning(int suggestedTimeOutMs) {
+ int timeOutMs = -1;
+ if (DBG) log("checkMobileProvisioning: E suggestedTimeOutMs=" + suggestedTimeOutMs);
+ enforceConnectivityInternalPermission();
final long token = Binder.clearCallingIdentity();
try {
+ timeOutMs = suggestedTimeOutMs;
+ if (suggestedTimeOutMs > CheckMp.MAX_TIMEOUT_MS) {
+ timeOutMs = CheckMp.MAX_TIMEOUT_MS;
+ }
+
+ // Check that mobile networks are supported
+ if (!isNetworkSupported(ConnectivityManager.TYPE_MOBILE)
+ || !isNetworkSupported(ConnectivityManager.TYPE_MOBILE_HIPRI)) {
+ if (DBG) log("checkMobileProvisioning: X no mobile network");
+ return timeOutMs;
+ }
+
+ // If we're already checking don't do it again
+ // TODO: Add a queue of results...
+ if (mIsCheckingMobileProvisioning.getAndSet(true)) {
+ if (DBG) log("checkMobileProvisioning: X already checking ignore for the moment");
+ return timeOutMs;
+ }
+
+ // Start off with notification off
+ setProvNotificationVisible(false, ConnectivityManager.TYPE_NONE, null, null);
+
+ // See if we've alreadying determined if we've got a provsioning connection
+ // if so we don't need to do anything active
+ MobileDataStateTracker mdstDefault = (MobileDataStateTracker)
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+ boolean isDefaultProvisioning = mdstDefault.isProvisioningNetwork();
+
+ MobileDataStateTracker mdstHipri = (MobileDataStateTracker)
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI];
+ boolean isHipriProvisioning = mdstHipri.isProvisioningNetwork();
+
+ if (isDefaultProvisioning || isHipriProvisioning) {
+ if (mIsNotificationVisible) {
+ if (DBG) {
+ log("checkMobileProvisioning: provisioning-ignore notification is visible");
+ }
+ } else {
+ NetworkInfo ni = null;
+ if (isDefaultProvisioning) {
+ ni = mdstDefault.getNetworkInfo();
+ }
+ if (isHipriProvisioning) {
+ ni = mdstHipri.getNetworkInfo();
+ }
+ String url = getMobileProvisioningUrl();
+ if ((ni != null) && (!TextUtils.isEmpty(url))) {
+ setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(), url);
+ } else {
+ if (DBG) log("checkMobileProvisioning: provisioning but no url, ignore");
+ }
+ }
+ mIsCheckingMobileProvisioning.set(false);
+ return timeOutMs;
+ }
+
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);
- }
+ if (DBG) log("CheckMp.onComplete: result=" + result);
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");
+ case CMP_RESULT_CODE_CONNECTABLE:
+ case CMP_RESULT_CODE_NO_CONNECTION: {
+ if (DBG) log("CheckMp.onComplete: ignore, connected or no connection");
break;
}
- case ConnectivityManager.CMP_RESULT_CODE_REDIRECTED: {
- log("CheckMp.onComplete: warm sim");
+ case CMP_RESULT_CODE_REDIRECTED: {
+ if (DBG) log("CheckMp.onComplete: warm sim");
String url = getMobileProvisioningUrl();
if (TextUtils.isEmpty(url)) {
url = getMobileRedirectedProvisioningUrl();
}
if (TextUtils.isEmpty(url) == false) {
- log("CheckMp.onComplete: warm sim (redirected), url=" + url);
- setNotificationVisible(true, ni, url);
+ if (DBG) log("CheckMp.onComplete: warm (redirected), url=" + url);
+ setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(),
+ url);
} else {
- log("CheckMp.onComplete: warm sim (redirected), no url");
+ if (DBG) log("CheckMp.onComplete: warm (redirected), no url");
}
break;
}
- case ConnectivityManager.CMP_RESULT_CODE_NO_DNS:
- case ConnectivityManager.CMP_RESULT_CODE_NO_TCP_CONNECTION: {
+ case CMP_RESULT_CODE_NO_DNS:
+ case CMP_RESULT_CODE_NO_TCP_CONNECTION: {
String url = getMobileProvisioningUrl();
if (TextUtils.isEmpty(url) == false) {
- log("CheckMp.onComplete: warm sim (no dns/tcp), url=" + url);
- setNotificationVisible(true, ni, url);
+ if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), url=" + url);
+ setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(),
+ url);
} else {
- log("CheckMp.onComplete: warm sim (no dns/tcp), no url");
+ if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), no url");
}
break;
}
break;
}
}
+ mIsCheckingMobileProvisioning.set(false);
}
};
CheckMp.Params params =
new CheckMp.Params(checkMp.getDefaultUrl(), timeOutMs, cb);
- log("checkMobileProvisioning: params=" + params);
- setNotificationVisible(false, null, null);
+ if (DBG) log("checkMobileProvisioning: params=" + params);
checkMp.execute(params);
} finally {
Binder.restoreCallingIdentity(token);
- log("checkMobileProvisioning: X");
+ if (DBG) log("checkMobileProvisioning: X");
}
return timeOutMs;
}
* a known address that fetches the data we expect.
*/
private synchronized Integer isMobileOk(Params params) {
- Integer result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+ Integer result = CMP_RESULT_CODE_NO_CONNECTION;
Uri orgUri = Uri.parse(params.mUrl);
Random rand = new Random();
mParams = params;
if (mCs.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
log("isMobileOk: not mobile capable");
- result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+ result = CMP_RESULT_CODE_NO_CONNECTION;
return result;
}
break;
}
if (VDBG) log("isMobileOk: hipri not started yet");
- result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+ result = CMP_RESULT_CODE_NO_CONNECTION;
sleep(1);
}
NetworkInfo.State state = mCs
.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
if (state != NetworkInfo.State.CONNECTED) {
- if (VDBG) {
+ if (true/*VDBG*/) {
log("isMobileOk: not connected ni=" +
mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
}
sleep(1);
- result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+ result = CMP_RESULT_CODE_NO_CONNECTION;
continue;
}
+ // Hipri has started check if this is a provisioning url
+ MobileDataStateTracker mdst = (MobileDataStateTracker)
+ mCs.mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI];
+ if (mdst.isProvisioningNetwork()) {
+ if (DBG) log("isMobileOk: isProvisioningNetwork is true, no TCP conn");
+ result = CMP_RESULT_CODE_NO_TCP_CONNECTION;
+ return result;
+ } else {
+ if (DBG) log("isMobileOk: isProvisioningNetwork is false, 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
addresses = InetAddress.getAllByName(orgUri.getHost());
} catch (UnknownHostException e) {
log("isMobileOk: UnknownHostException");
- result = ConnectivityManager.CMP_RESULT_CODE_NO_DNS;
+ result = CMP_RESULT_CODE_NO_DNS;
return result;
}
log("isMobileOk: addresses=" + inetAddressesToString(addresses));
urlConn.setRequestProperty("Connection", "close");
int responseCode = urlConn.getResponseCode();
if (responseCode == 204) {
- result = ConnectivityManager.CMP_RESULT_CODE_CONNECTABLE;
+ result = CMP_RESULT_CODE_CONNECTABLE;
} else {
- result = ConnectivityManager.CMP_RESULT_CODE_REDIRECTED;
+ result = CMP_RESULT_CODE_REDIRECTED;
}
log("isMobileOk: connected responseCode=" + responseCode);
urlConn.disconnect();
}
}
}
- result = ConnectivityManager.CMP_RESULT_CODE_NO_TCP_CONNECTION;
+ result = CMP_RESULT_CODE_NO_TCP_CONNECTION;
log("isMobileOk: loops|timed out");
return result;
} catch (Exception e) {
mCs.setEnableFailFastMobileData(DctConstants.DISABLED);
mCs.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
Phone.FEATURE_ENABLE_HIPRI);
+
+ // Wait for hipri to disconnect.
+ long endTime = SystemClock.elapsedRealtime() + 5000;
+
+ while(SystemClock.elapsedRealtime() < endTime) {
+ NetworkInfo.State state = mCs
+ .getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
+ if (state != NetworkInfo.State.DISCONNECTED) {
+ if (VDBG) {
+ log("isMobileOk: connected ni=" +
+ mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
+ }
+ sleep(1);
+ continue;
+ }
+ }
+
log("isMobileOk: X result=" + result);
}
return result;
}
}
+ // TODO: Move to ConnectivityManager and make public?
+ private static final String CONNECTED_TO_PROVISIONING_NETWORK_ACTION =
+ "com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION";
+
+ private BroadcastReceiver mProvisioningReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(CONNECTED_TO_PROVISIONING_NETWORK_ACTION)) {
+ handleMobileProvisioningAction(intent.getStringExtra("EXTRA_URL"));
+ }
+ }
+ };
+
+ private void handleMobileProvisioningAction(String url) {
+ // Notication mark notification as not visible
+ setProvNotificationVisible(false, ConnectivityManager.TYPE_NONE, null, null);
+
+ // If provisioning network handle as a special case,
+ // otherwise launch browser with the intent directly.
+ NetworkInfo ni = getProvisioningNetworkInfo();
+ if ((ni != null) && ni.getDetailedState() ==
+ NetworkInfo.DetailedState.CONNECTED_TO_PROVISIONING_NETWORK) {
+ if (DBG) log("handleMobileProvisioningAction: on provisioning network");
+ MobileDataStateTracker mdst = (MobileDataStateTracker)
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+ mdst.enableMobileProvisioning(url);
+ } else {
+ if (DBG) log("handleMobileProvisioningAction: on default network");
+ Intent newIntent =
+ new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ newIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mContext.startActivity(newIntent);
+ } catch (ActivityNotFoundException e) {
+ loge("handleMobileProvisioningAction: startActivity failed" + e);
+ }
+ }
+ }
+
private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
+ private volatile boolean mIsNotificationVisible = false;
- private void setNotificationVisible(boolean visible, NetworkInfo networkInfo, String url) {
- log("setNotificationVisible: E visible=" + visible + " ni=" + networkInfo + " url=" + url);
+ private void setProvNotificationVisible(boolean visible, int networkType, String extraInfo,
+ String url) {
+ if (DBG) {
+ log("setProvNotificationVisible: E visible=" + visible + " networkType=" + networkType
+ + " extraInfo=" + extraInfo + " url=" + url);
+ }
Resources r = Resources.getSystem();
NotificationManager notificationManager = (NotificationManager) mContext
CharSequence title;
CharSequence details;
int icon;
- switch (networkInfo.getType()) {
+ Intent intent;
+ Notification notification = new Notification();
+ switch (networkType) {
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());
+ extraInfo);
icon = R.drawable.stat_notify_wifi_in_range;
+ 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);
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;
+ intent = new Intent(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
+ intent.putExtra("EXTRA_URL", url);
+ intent.setFlags(0);
+ notification.contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
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());
+ extraInfo);
icon = R.drawable.stat_notify_rssi_in_range;
+ 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);
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);
+ try {
+ notificationManager.notify(NOTIFICATION_ID, 1, notification);
+ } catch (NullPointerException npe) {
+ loge("setNotificaitionVisible: visible notificationManager npe=" + npe);
+ npe.printStackTrace();
+ }
} else {
- log("setNotificaitionVisible: cancel");
- notificationManager.cancel(NOTIFICATION_ID, 1);
+ try {
+ notificationManager.cancel(NOTIFICATION_ID, 1);
+ } catch (NullPointerException npe) {
+ loge("setNotificaitionVisible: cancel notificationManager npe=" + npe);
+ npe.printStackTrace();
+ }
}
- log("setNotificationVisible: X visible=" + visible + " ni=" + networkInfo + " url=" + url);
+ mIsNotificationVisible = visible;
}
/** Location to an updatable file listing carrier provisioning urls.
return url;
}
+
+ @Override
+ public void setProvisioningNotificationVisible(boolean visible, int networkType,
+ String extraInfo, String url) {
+ enforceConnectivityInternalPermission();
+ setProvNotificationVisible(visible, networkType, extraInfo, url);
+ }
}