Add support for handling mobile provisioning networks.
When a sim is new or it has expired it needs to be provisioned
with the carrier. Basically provisioning is associating a sim with
a user account. When a sim isn't provisioned then operators will
restrict access to the network and only allow certain addresses
or services to be used.
This set of changes allows two types of provisioning networks to be
recognized. The first is a network that causes all DNS lookups to be
redirected to a different address than was intended. This is exemplified
by how T-Mobile works.
The second technique uses a special apn for provisioning. An example is
AT&T where lwaactivate is the provisioning apn and broadband is the
normal apn. We first try broadband and if we are unable to connect we
try lwaactivate. When we see the activate we identify it as special and
the ApnContext.isProvisioningApn will return true.
In the future our plan is to create a new network type that can be added
to the apn list, but for now it identified by name.
Here is a list of significant changes:
- CaptivePortalTracker now only test WiFi networks instead of all networks
- checkMobileProvisioning checks for provisioning networks and doesn't
try to ping.
- IConnectivityManager.aidl changes:
* getProvisioningOrActiveNetworkInfo was added to and used by Manage
mobile plan in WirelessSettings so even when there is no active
network it will still allow provisioning. Otherwise it would report
no internet connection.
* setSignInErrorNotificationVisible is used by both
CaptiviePortalTracker and checkMobileProvisioning so they use the
same code for the notifications.
* checkMobileProvisioning was simplified to have only a timeout as
returning the result is now harder as we abort simultaneous call
otherwise we'd could get into loops because we now check every time
we connect to mobile.
- Enhanced MDST to handle the provisioning network.
- Added CONNECTED_TO_PROVISIONING_NETWORK to NetworkInfo to make a new
state so we don't announce to the world we're connected.
- TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN
is sent by the low level data connection code to notify Connectivity
Service that a provisioning apn has connected. This allows CS to
handle the connection differently than a normal connection.
Bug: 10328264
Change-Id: I3925004011bb1243793c4c1b963d923dc2b00cb5
This commit is contained in:
@@ -58,13 +58,11 @@ public class CaptivePortalTracker extends StateMachine {
|
|||||||
private static final String TAG = "CaptivePortalTracker";
|
private static final String TAG = "CaptivePortalTracker";
|
||||||
|
|
||||||
private static final String DEFAULT_SERVER = "clients3.google.com";
|
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 static final int SOCKET_TIMEOUT_MS = 10000;
|
||||||
|
|
||||||
private String mServer;
|
private String mServer;
|
||||||
private String mUrl;
|
private String mUrl;
|
||||||
private boolean mNotificationShown = false;
|
|
||||||
private boolean mIsCaptivePortalCheckEnabled = false;
|
private boolean mIsCaptivePortalCheckEnabled = false;
|
||||||
private IConnectivityManager mConnService;
|
private IConnectivityManager mConnService;
|
||||||
private TelephonyManager mTelephonyManager;
|
private TelephonyManager mTelephonyManager;
|
||||||
@@ -161,12 +159,12 @@ public class CaptivePortalTracker extends StateMachine {
|
|||||||
private class DefaultState extends State {
|
private class DefaultState extends State {
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
if (DBG) log(getName() + "\n");
|
setNotificationOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processMessage(Message message) {
|
public boolean processMessage(Message message) {
|
||||||
if (DBG) log(getName() + message.toString() + "\n");
|
if (DBG) log(getName() + message.toString());
|
||||||
switch (message.what) {
|
switch (message.what) {
|
||||||
case CMD_DETECT_PORTAL:
|
case CMD_DETECT_PORTAL:
|
||||||
NetworkInfo info = (NetworkInfo) message.obj;
|
NetworkInfo info = (NetworkInfo) message.obj;
|
||||||
@@ -188,24 +186,25 @@ public class CaptivePortalTracker extends StateMachine {
|
|||||||
private class NoActiveNetworkState extends State {
|
private class NoActiveNetworkState extends State {
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
if (DBG) log(getName() + "\n");
|
|
||||||
mNetworkInfo = null;
|
mNetworkInfo = null;
|
||||||
/* Clear any previous notification */
|
|
||||||
setNotificationVisible(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processMessage(Message message) {
|
public boolean processMessage(Message message) {
|
||||||
if (DBG) log(getName() + message.toString() + "\n");
|
if (DBG) log(getName() + message.toString());
|
||||||
InetAddress server;
|
InetAddress server;
|
||||||
NetworkInfo info;
|
NetworkInfo info;
|
||||||
switch (message.what) {
|
switch (message.what) {
|
||||||
case CMD_CONNECTIVITY_CHANGE:
|
case CMD_CONNECTIVITY_CHANGE:
|
||||||
info = (NetworkInfo) message.obj;
|
info = (NetworkInfo) message.obj;
|
||||||
|
if (info.getType() == ConnectivityManager.TYPE_WIFI) {
|
||||||
if (info.isConnected() && isActiveNetwork(info)) {
|
if (info.isConnected() && isActiveNetwork(info)) {
|
||||||
mNetworkInfo = info;
|
mNetworkInfo = info;
|
||||||
transitionTo(mDelayedCaptiveCheckState);
|
transitionTo(mDelayedCaptiveCheckState);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log(getName() + " not a wifi connectivity change, ignore");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return NOT_HANDLED;
|
return NOT_HANDLED;
|
||||||
@@ -217,7 +216,7 @@ public class CaptivePortalTracker extends StateMachine {
|
|||||||
private class ActiveNetworkState extends State {
|
private class ActiveNetworkState extends State {
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
if (DBG) log(getName() + "\n");
|
setNotificationOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -250,7 +249,6 @@ public class CaptivePortalTracker extends StateMachine {
|
|||||||
private class DelayedCaptiveCheckState extends State {
|
private class DelayedCaptiveCheckState extends State {
|
||||||
@Override
|
@Override
|
||||||
public void enter() {
|
public void enter() {
|
||||||
if (DBG) log(getName() + "\n");
|
|
||||||
Message message = obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, ++mDelayedCheckToken, 0);
|
Message message = obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, ++mDelayedCheckToken, 0);
|
||||||
if (mDeviceProvisioned) {
|
if (mDeviceProvisioned) {
|
||||||
sendMessageDelayed(message, DELAYED_CHECK_INTERVAL_MS);
|
sendMessageDelayed(message, DELAYED_CHECK_INTERVAL_MS);
|
||||||
@@ -261,7 +259,7 @@ public class CaptivePortalTracker extends StateMachine {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processMessage(Message message) {
|
public boolean processMessage(Message message) {
|
||||||
if (DBG) log(getName() + message.toString() + "\n");
|
if (DBG) log(getName() + message.toString());
|
||||||
switch (message.what) {
|
switch (message.what) {
|
||||||
case CMD_DELAYED_CAPTIVE_CHECK:
|
case CMD_DELAYED_CAPTIVE_CHECK:
|
||||||
if (message.arg1 == mDelayedCheckToken) {
|
if (message.arg1 == mDelayedCheckToken) {
|
||||||
@@ -277,7 +275,12 @@ public class CaptivePortalTracker extends StateMachine {
|
|||||||
if (captive) {
|
if (captive) {
|
||||||
// Setup Wizard will assist the user in connecting to a captive
|
// Setup Wizard will assist the user in connecting to a captive
|
||||||
// portal, so make the notification visible unless during setup
|
// 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 {
|
} else {
|
||||||
Intent intent = new Intent(
|
Intent intent = new Intent(
|
||||||
@@ -335,6 +338,15 @@ public class CaptivePortalTracker extends StateMachine {
|
|||||||
return false;
|
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
|
* Do a URL fetch on a known server to see if we get the data we expect
|
||||||
*/
|
*/
|
||||||
@@ -354,9 +366,6 @@ public class CaptivePortalTracker extends StateMachine {
|
|||||||
urlConnection.getInputStream();
|
urlConnection.getInputStream();
|
||||||
// we got a valid response, but not from the real google
|
// we got a valid response, but not from the real google
|
||||||
return urlConnection.getResponseCode() != 204;
|
return urlConnection.getResponseCode() != 204;
|
||||||
} catch (SocketTimeoutException e) {
|
|
||||||
if (DBG) log("Probably a portal: exception " + e);
|
|
||||||
return true;
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (DBG) log("Probably not a portal: exception " + e);
|
if (DBG) log("Probably not a portal: exception " + e);
|
||||||
return false;
|
return false;
|
||||||
@@ -380,75 +389,4 @@ public class CaptivePortalTracker extends StateMachine {
|
|||||||
}
|
}
|
||||||
return null;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -582,6 +582,29 @@ public class ConnectivityManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns details about the Provisioning or currently active default data network. When
|
||||||
|
* connected, this network is the default route for outgoing connections.
|
||||||
|
* You should always check {@link NetworkInfo#isConnected()} before initiating
|
||||||
|
* network traffic. This may return {@code null} when there is no default
|
||||||
|
* network.
|
||||||
|
*
|
||||||
|
* @return a {@link NetworkInfo} object for the current default network
|
||||||
|
* or {@code null} if no network default network is currently active
|
||||||
|
*
|
||||||
|
* <p>This method requires the call to hold the permission
|
||||||
|
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
|
||||||
|
*
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
public NetworkInfo getProvisioningOrActiveNetworkInfo() {
|
||||||
|
try {
|
||||||
|
return mService.getProvisioningOrActiveNetworkInfo();
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IP information for the current default network.
|
* Returns the IP information for the current default network.
|
||||||
*
|
*
|
||||||
@@ -1316,63 +1339,19 @@ public class ConnectivityManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ResultReceiver resultCode for checkMobileProvisioning (CMP_RESULT_CODE)
|
* Check mobile provisioning.
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 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
|
* @return time out that will be used, maybe less that suggestedTimeOutMs
|
||||||
* -1 if an error.
|
* -1 if an error.
|
||||||
*
|
*
|
||||||
* {@hide}
|
* {@hide}
|
||||||
*/
|
*/
|
||||||
public int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs,
|
public int checkMobileProvisioning(int suggestedTimeOutMs) {
|
||||||
ResultReceiver resultReceiver) {
|
|
||||||
int timeOutMs = -1;
|
int timeOutMs = -1;
|
||||||
try {
|
try {
|
||||||
timeOutMs = mService.checkMobileProvisioning(sendNotification, suggestedTimeOutMs,
|
timeOutMs = mService.checkMobileProvisioning(suggestedTimeOutMs);
|
||||||
resultReceiver);
|
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
}
|
}
|
||||||
return timeOutMs;
|
return timeOutMs;
|
||||||
@@ -1401,4 +1380,20 @@ public class ConnectivityManager {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set sign in error notification to visible or in visible
|
||||||
|
*
|
||||||
|
* @param visible
|
||||||
|
* @param networkType
|
||||||
|
*
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
public void setProvisioningNotificationVisible(boolean visible, int networkType,
|
||||||
|
String extraInfo, String url) {
|
||||||
|
try {
|
||||||
|
mService.setProvisioningNotificationVisible(visible, networkType, extraInfo, url);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ interface IConnectivityManager
|
|||||||
NetworkInfo getNetworkInfo(int networkType);
|
NetworkInfo getNetworkInfo(int networkType);
|
||||||
NetworkInfo[] getAllNetworkInfo();
|
NetworkInfo[] getAllNetworkInfo();
|
||||||
|
|
||||||
|
NetworkInfo getProvisioningOrActiveNetworkInfo();
|
||||||
|
|
||||||
boolean isNetworkSupported(int networkType);
|
boolean isNetworkSupported(int networkType);
|
||||||
|
|
||||||
LinkProperties getActiveLinkProperties();
|
LinkProperties getActiveLinkProperties();
|
||||||
@@ -135,9 +137,11 @@ interface IConnectivityManager
|
|||||||
|
|
||||||
int findConnectionTypeForIface(in String iface);
|
int findConnectionTypeForIface(in String iface);
|
||||||
|
|
||||||
int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs, in ResultReceiver resultReceiver);
|
int checkMobileProvisioning(int suggestedTimeOutMs);
|
||||||
|
|
||||||
String getMobileProvisioningUrl();
|
String getMobileProvisioningUrl();
|
||||||
|
|
||||||
String getMobileRedirectedProvisioningUrl();
|
String getMobileRedirectedProvisioningUrl();
|
||||||
|
|
||||||
|
void setProvisioningNotificationVisible(boolean visible, int networkType, in String extraInfo, in String url);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
public class MobileDataStateTracker implements NetworkStateTracker {
|
public class MobileDataStateTracker implements NetworkStateTracker {
|
||||||
|
|
||||||
private static final String TAG = "MobileDataStateTracker";
|
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 static final boolean VDBG = false;
|
||||||
|
|
||||||
private PhoneConstants.DataState mMobileDataState;
|
private PhoneConstants.DataState mMobileDataState;
|
||||||
@@ -104,6 +104,7 @@ public class MobileDataStateTracker implements NetworkStateTracker {
|
|||||||
|
|
||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
|
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);
|
filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
|
||||||
|
|
||||||
mContext.registerReceiver(new MobileDataStateReceiver(), filter);
|
mContext.registerReceiver(new MobileDataStateReceiver(), filter);
|
||||||
@@ -171,20 +172,49 @@ public class MobileDataStateTracker implements NetworkStateTracker {
|
|||||||
public void releaseWakeLock() {
|
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 {
|
private class MobileDataStateReceiver extends BroadcastReceiver {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (intent.getAction().equals(TelephonyIntents.
|
if (intent.getAction().equals(TelephonyIntents.
|
||||||
|
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 (!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)) {
|
ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
|
||||||
String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_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(apnType, mApnType)) {
|
if (!TextUtils.equals(apnType, mApnType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (DBG) {
|
||||||
|
log("Broadcast received: " + intent.getAction() + " apnType=" + apnType);
|
||||||
|
}
|
||||||
|
|
||||||
int oldSubtype = mNetworkInfo.getSubtype();
|
int oldSubtype = mNetworkInfo.getSubtype();
|
||||||
int newSubType = TelephonyManager.getDefault().getNetworkType();
|
int newSubType = TelephonyManager.getDefault().getNetworkType();
|
||||||
@@ -202,7 +232,7 @@ public class MobileDataStateTracker implements NetworkStateTracker {
|
|||||||
String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
|
String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
|
||||||
mNetworkInfo.setRoaming(intent.getBooleanExtra(
|
mNetworkInfo.setRoaming(intent.getBooleanExtra(
|
||||||
PhoneConstants.DATA_NETWORK_ROAMING_KEY, false));
|
PhoneConstants.DATA_NETWORK_ROAMING_KEY, false));
|
||||||
if (VDBG) {
|
if (DBG) {
|
||||||
log(mApnType + " setting isAvailable to " +
|
log(mApnType + " setting isAvailable to " +
|
||||||
intent.getBooleanExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY,false));
|
intent.getBooleanExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY,false));
|
||||||
}
|
}
|
||||||
@@ -236,18 +266,7 @@ public class MobileDataStateTracker implements NetworkStateTracker {
|
|||||||
setDetailedState(DetailedState.SUSPENDED, reason, apnName);
|
setDetailedState(DetailedState.SUSPENDED, reason, apnName);
|
||||||
break;
|
break;
|
||||||
case CONNECTED:
|
case CONNECTED:
|
||||||
mLinkProperties = intent.getParcelableExtra(
|
updateLinkProperitesAndCapatilities(intent);
|
||||||
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();
|
|
||||||
}
|
|
||||||
setDetailedState(DetailedState.CONNECTED, reason, apnName);
|
setDetailedState(DetailedState.CONNECTED, reason, apnName);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -272,18 +291,13 @@ public class MobileDataStateTracker implements NetworkStateTracker {
|
|||||||
equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
|
equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
|
||||||
String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
|
String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
|
||||||
if (!TextUtils.equals(apnType, mApnType)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY);
|
String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY);
|
||||||
String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
|
String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
|
||||||
if (DBG) {
|
if (DBG) {
|
||||||
log("Received " + intent.getAction() +
|
log("Broadcast received: " + intent.getAction() +
|
||||||
" broadcast" + reason == null ? "" : "(" + reason + ")");
|
" reason=" + reason == null ? "null" : reason);
|
||||||
}
|
}
|
||||||
setDetailedState(DetailedState.FAILED, reason, apnName);
|
setDetailedState(DetailedState.FAILED, reason, apnName);
|
||||||
} else {
|
} else {
|
||||||
@@ -544,6 +558,40 @@ public class MobileDataStateTracker implements NetworkStateTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
@Override
|
||||||
public void addStackedLink(LinkProperties link) {
|
public void addStackedLink(LinkProperties link) {
|
||||||
mLinkProperties.addStackedLink(link);
|
mLinkProperties.addStackedLink(link);
|
||||||
|
|||||||
@@ -84,6 +84,12 @@ public class NetworkInfo implements Parcelable {
|
|||||||
VERIFYING_POOR_LINK,
|
VERIFYING_POOR_LINK,
|
||||||
/** Checking if network is a captive portal */
|
/** Checking if network is a captive portal */
|
||||||
CAPTIVE_PORTAL_CHECK,
|
CAPTIVE_PORTAL_CHECK,
|
||||||
|
/**
|
||||||
|
* Network is connected to provisioning network
|
||||||
|
* TODO: Probably not needed when we add TYPE_PROVISIONING_NETWORK
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
CONNECTED_TO_PROVISIONING_NETWORK
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -108,6 +114,7 @@ public class NetworkInfo implements Parcelable {
|
|||||||
stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED);
|
stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED);
|
||||||
stateMap.put(DetailedState.FAILED, State.DISCONNECTED);
|
stateMap.put(DetailedState.FAILED, State.DISCONNECTED);
|
||||||
stateMap.put(DetailedState.BLOCKED, State.DISCONNECTED);
|
stateMap.put(DetailedState.BLOCKED, State.DISCONNECTED);
|
||||||
|
stateMap.put(DetailedState.CONNECTED_TO_PROVISIONING_NETWORK, State.CONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int mNetworkType;
|
private int mNetworkType;
|
||||||
|
|||||||
@@ -151,6 +151,7 @@
|
|||||||
<protected-broadcast android:name="android.intent.action.DREAMING_STARTED" />
|
<protected-broadcast android:name="android.intent.action.DREAMING_STARTED" />
|
||||||
<protected-broadcast android:name="android.intent.action.DREAMING_STOPPED" />
|
<protected-broadcast android:name="android.intent.action.DREAMING_STOPPED" />
|
||||||
<protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
|
<protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
|
||||||
|
<protected-broadcast android:name="android.intent.action.DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN" />
|
||||||
|
|
||||||
<protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" />
|
<protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" />
|
||||||
<protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
|
<protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
|
||||||
@@ -181,6 +182,9 @@
|
|||||||
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
|
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
|
||||||
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
|
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
|
||||||
|
|
||||||
|
<protected-broadcast
|
||||||
|
android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
|
||||||
|
|
||||||
<!-- ====================================== -->
|
<!-- ====================================== -->
|
||||||
<!-- Permissions for things that cost money -->
|
<!-- Permissions for things that cost money -->
|
||||||
<!-- ====================================== -->
|
<!-- ====================================== -->
|
||||||
|
|||||||
@@ -772,6 +772,10 @@
|
|||||||
<!-- IP address of the dns server to use if nobody else suggests one -->
|
<!-- 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>
|
<string name="config_default_dns_server" translatable="false">8.8.8.8</string>
|
||||||
|
|
||||||
|
<!-- The default mobile provisioning apn. Empty by default, maybe overridden by
|
||||||
|
an mcc/mnc specific config.xml -->
|
||||||
|
<string name="mobile_provisioning_apn" translatable="false"></string>
|
||||||
|
|
||||||
<!-- The default mobile provisioning url. Empty by default, maybe overridden by
|
<!-- The default mobile provisioning url. Empty by default, maybe overridden by
|
||||||
an mcc/mnc specific config.xml -->
|
an mcc/mnc specific config.xml -->
|
||||||
<string name="mobile_provisioning_url" translatable="false"></string>
|
<string name="mobile_provisioning_url" translatable="false"></string>
|
||||||
|
|||||||
@@ -703,6 +703,7 @@
|
|||||||
<java-symbol type="string" name="preposition_for_time" />
|
<java-symbol type="string" name="preposition_for_time" />
|
||||||
<java-symbol type="string" name="progress_erasing" />
|
<java-symbol type="string" name="progress_erasing" />
|
||||||
<java-symbol type="string" name="progress_unmounting" />
|
<java-symbol type="string" name="progress_unmounting" />
|
||||||
|
<java-symbol type="string" name="mobile_provisioning_apn" />
|
||||||
<java-symbol type="string" name="mobile_provisioning_url" />
|
<java-symbol type="string" name="mobile_provisioning_url" />
|
||||||
<java-symbol type="string" name="mobile_redirected_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_confirm" />
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import android.app.Notification;
|
|||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.bluetooth.BluetoothTetheringDataTracker;
|
import android.bluetooth.BluetoothTetheringDataTracker;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -143,6 +144,7 @@ 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.Random;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -383,9 +385,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
|
|
||||||
TelephonyManager mTelephonyManager;
|
TelephonyManager mTelephonyManager;
|
||||||
|
|
||||||
// We only want one checkMobileProvisioning after booting.
|
|
||||||
volatile boolean mFirstProvisioningCheckStarted = false;
|
|
||||||
|
|
||||||
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
|
||||||
@@ -609,8 +608,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
|
mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
|
||||||
mSettingsObserver.observe(mContext);
|
mSettingsObserver.observe(mContext);
|
||||||
|
|
||||||
mCaptivePortalTracker = CaptivePortalTracker.makeCaptivePortalTracker(mContext, this);
|
IntentFilter filter = new IntentFilter();
|
||||||
loadGlobalProxy();
|
filter.addAction(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
|
||||||
|
mContext.registerReceiver(mProvisioningReceiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -879,6 +879,46 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
return getNetworkInfo(mActiveDefaultNetwork, uid);
|
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() {
|
public NetworkInfo getActiveNetworkInfoUnfiltered() {
|
||||||
enforceAccessPermission();
|
enforceAccessPermission();
|
||||||
if (isNetworkTypeValid(mActiveDefaultNetwork)) {
|
if (isNetworkTypeValid(mActiveDefaultNetwork)) {
|
||||||
@@ -1241,8 +1281,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
feature);
|
feature);
|
||||||
}
|
}
|
||||||
if (network.reconnect()) {
|
if (network.reconnect()) {
|
||||||
|
if (DBG) log("startUsingNetworkFeature X: return APN_REQUEST_STARTED");
|
||||||
return PhoneConstants.APN_REQUEST_STARTED;
|
return PhoneConstants.APN_REQUEST_STARTED;
|
||||||
} else {
|
} else {
|
||||||
|
if (DBG) log("startUsingNetworkFeature X: return APN_REQUEST_FAILED");
|
||||||
return PhoneConstants.APN_REQUEST_FAILED;
|
return PhoneConstants.APN_REQUEST_FAILED;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1254,9 +1296,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
mNetRequestersPids[usedNetworkType].add(currentPid);
|
mNetRequestersPids[usedNetworkType].add(currentPid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (DBG) log("startUsingNetworkFeature X: return -1 unsupported feature.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (DBG) log("startUsingNetworkFeature X: return APN_TYPE_NOT_AVAILABLE");
|
||||||
return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
|
return PhoneConstants.APN_TYPE_NOT_AVAILABLE;
|
||||||
} finally {
|
} finally {
|
||||||
if (DBG) {
|
if (DBG) {
|
||||||
@@ -1290,11 +1334,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found && u != null) {
|
if (found && u != null) {
|
||||||
|
if (VDBG) log("stopUsingNetworkFeature: X");
|
||||||
// stop regardless of how many other time this proc had called start
|
// stop regardless of how many other time this proc had called start
|
||||||
return stopUsingNetworkFeature(u, true);
|
return stopUsingNetworkFeature(u, true);
|
||||||
} else {
|
} else {
|
||||||
// none found!
|
// none found!
|
||||||
if (VDBG) log("stopUsingNetworkFeature - not a live request, ignoring");
|
if (VDBG) log("stopUsingNetworkFeature: X not a live request, ignoring");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1849,6 +1894,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
*/
|
*/
|
||||||
if (mNetConfigs[prevNetType].isDefault()) {
|
if (mNetConfigs[prevNetType].isDefault()) {
|
||||||
if (mActiveDefaultNetwork == prevNetType) {
|
if (mActiveDefaultNetwork == prevNetType) {
|
||||||
|
if (DBG) {
|
||||||
|
log("tryFailover: set mActiveDefaultNetwork=-1, prevNetType=" + prevNetType);
|
||||||
|
}
|
||||||
mActiveDefaultNetwork = -1;
|
mActiveDefaultNetwork = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2041,6 +2089,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void systemReady() {
|
void systemReady() {
|
||||||
|
mCaptivePortalTracker = CaptivePortalTracker.makeCaptivePortalTracker(mContext, this);
|
||||||
|
loadGlobalProxy();
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
mSystemReady = true;
|
mSystemReady = true;
|
||||||
if (mInitialBroadcast != null) {
|
if (mInitialBroadcast != null) {
|
||||||
@@ -2071,10 +2122,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private boolean isNewNetTypePreferredOverCurrentNetType(int type) {
|
private boolean isNewNetTypePreferredOverCurrentNetType(int type) {
|
||||||
if ((type != mNetworkPreference &&
|
if (((type != mNetworkPreference)
|
||||||
mNetConfigs[mActiveDefaultNetwork].priority >
|
&& (mNetConfigs[mActiveDefaultNetwork].priority > mNetConfigs[type].priority))
|
||||||
mNetConfigs[type].priority) ||
|
|| (mNetworkPreference == mActiveDefaultNetwork)) {
|
||||||
mNetworkPreference == mActiveDefaultNetwork) return false;
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2088,6 +2140,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
final NetworkStateTracker thisNet = mNetTrackers[newNetType];
|
final NetworkStateTracker thisNet = mNetTrackers[newNetType];
|
||||||
final String thisIface = thisNet.getLinkProperties().getInterfaceName();
|
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
|
// if this is a default net and other default is running
|
||||||
// kill the one not preferred
|
// kill the one not preferred
|
||||||
if (mNetConfigs[newNetType].isDefault()) {
|
if (mNetConfigs[newNetType].isDefault()) {
|
||||||
@@ -2249,6 +2306,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
*/
|
*/
|
||||||
private void handleConnectivityChange(int netType, boolean doReset) {
|
private void handleConnectivityChange(int netType, boolean doReset) {
|
||||||
int resetMask = doReset ? NetworkUtils.RESET_ALL_ADDRESSES : 0;
|
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
|
* If a non-default network is enabled, add the host routes that
|
||||||
@@ -2316,7 +2377,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault());
|
boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault());
|
||||||
|
|
||||||
if (resetMask != 0 || resetDns) {
|
if (resetMask != 0 || resetDns) {
|
||||||
|
if (VDBG) log("handleConnectivityChange: resetting");
|
||||||
if (curLp != null) {
|
if (curLp != null) {
|
||||||
|
if (VDBG) log("handleConnectivityChange: resetting curLp=" + curLp);
|
||||||
for (String iface : curLp.getAllInterfaceNames()) {
|
for (String iface : curLp.getAllInterfaceNames()) {
|
||||||
if (TextUtils.isEmpty(iface) == false) {
|
if (TextUtils.isEmpty(iface) == false) {
|
||||||
if (resetMask != 0) {
|
if (resetMask != 0) {
|
||||||
@@ -2349,6 +2412,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
// Update 464xlat state.
|
// Update 464xlat state.
|
||||||
NetworkStateTracker tracker = mNetTrackers[netType];
|
NetworkStateTracker tracker = mNetTrackers[netType];
|
||||||
if (mClat.requiresClat(netType, tracker)) {
|
if (mClat.requiresClat(netType, tracker)) {
|
||||||
|
|
||||||
// If the connection was previously using clat, but is not using it now, stop the clat
|
// 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
|
// daemon. Normally, this happens automatically when the connection disconnects, but if
|
||||||
// the disconnect is not reported, or if the connection's LinkProperties changed for
|
// the disconnect is not reported, or if the connection's LinkProperties changed for
|
||||||
@@ -2402,6 +2466,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
|
|
||||||
for (RouteInfo r : routeDiff.removed) {
|
for (RouteInfo r : routeDiff.removed) {
|
||||||
if (isLinkDefault || ! r.isDefaultRoute()) {
|
if (isLinkDefault || ! r.isDefaultRoute()) {
|
||||||
|
if (VDBG) log("updateRoutes: default remove route r=" + r);
|
||||||
removeRoute(curLp, r, TO_DEFAULT_TABLE);
|
removeRoute(curLp, r, TO_DEFAULT_TABLE);
|
||||||
}
|
}
|
||||||
if (isLinkDefault == false) {
|
if (isLinkDefault == false) {
|
||||||
@@ -2446,7 +2511,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
// remove the default route unless somebody else has asked for it
|
// remove the default route unless somebody else has asked for it
|
||||||
String ifaceName = newLp.getInterfaceName();
|
String ifaceName = newLp.getInterfaceName();
|
||||||
if (TextUtils.isEmpty(ifaceName) == false && mAddedRoutes.contains(r) == false) {
|
if (TextUtils.isEmpty(ifaceName) == false && mAddedRoutes.contains(r) == false) {
|
||||||
if (VDBG) log("Removing " + r + " for interface " + ifaceName);
|
|
||||||
try {
|
try {
|
||||||
mNetd.removeRoute(ifaceName, r);
|
mNetd.removeRoute(ifaceName, r);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -2736,9 +2800,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
NetworkInfo info;
|
NetworkInfo info;
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case NetworkStateTracker.EVENT_STATE_CHANGED:
|
case NetworkStateTracker.EVENT_STATE_CHANGED: {
|
||||||
info = (NetworkInfo) msg.obj;
|
info = (NetworkInfo) msg.obj;
|
||||||
int type = info.getType();
|
|
||||||
NetworkInfo.State state = info.getState();
|
NetworkInfo.State state = info.getState();
|
||||||
|
|
||||||
if (VDBG || (state == NetworkInfo.State.CONNECTED) ||
|
if (VDBG || (state == NetworkInfo.State.CONNECTED) ||
|
||||||
@@ -2748,15 +2811,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
state + "/" + info.getDetailedState());
|
state + "/" + info.getDetailedState());
|
||||||
}
|
}
|
||||||
|
|
||||||
// After booting we'll check once for mobile provisioning
|
// Since mobile has the notion of a network/apn that can be used for
|
||||||
// if we've provisioned by and connected.
|
// provisioning we need to check every time we're connected as
|
||||||
if (!mFirstProvisioningCheckStarted
|
// 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(),
|
&& (0 != Settings.Global.getInt(mContext.getContentResolver(),
|
||||||
Settings.Global.DEVICE_PROVISIONED, 0))
|
Settings.Global.DEVICE_PROVISIONED, 0))
|
||||||
&& (state == NetworkInfo.State.CONNECTED)) {
|
&& (state == NetworkInfo.State.CONNECTED)) {
|
||||||
log("check provisioning after booting");
|
checkMobileProvisioning(CheckMp.MAX_TIMEOUT_MS);
|
||||||
mFirstProvisioningCheckStarted = true;
|
|
||||||
checkMobileProvisioning(true, CheckMp.MAX_TIMEOUT_MS, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EventLogTags.writeConnectivityStateChanged(
|
EventLogTags.writeConnectivityStateChanged(
|
||||||
@@ -2768,6 +2833,30 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
} else if (info.getDetailedState() ==
|
} else if (info.getDetailedState() ==
|
||||||
DetailedState.CAPTIVE_PORTAL_CHECK) {
|
DetailedState.CAPTIVE_PORTAL_CHECK) {
|
||||||
handleCaptivePortalTrackerCheck(info);
|
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) {
|
} else if (state == NetworkInfo.State.DISCONNECTED) {
|
||||||
handleDisconnect(info);
|
handleDisconnect(info);
|
||||||
} else if (state == NetworkInfo.State.SUSPENDED) {
|
} else if (state == NetworkInfo.State.SUSPENDED) {
|
||||||
@@ -2786,21 +2875,24 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
mLockdownTracker.onNetworkInfoChanged(info);
|
mLockdownTracker.onNetworkInfoChanged(info);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
|
}
|
||||||
|
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: {
|
||||||
info = (NetworkInfo) msg.obj;
|
info = (NetworkInfo) msg.obj;
|
||||||
// TODO: Temporary allowing network configuration
|
// TODO: Temporary allowing network configuration
|
||||||
// change not resetting sockets.
|
// change not resetting sockets.
|
||||||
// @see bug/4455071
|
// @see bug/4455071
|
||||||
handleConnectivityChange(info.getType(), false);
|
handleConnectivityChange(info.getType(), false);
|
||||||
break;
|
break;
|
||||||
case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
|
}
|
||||||
|
case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: {
|
||||||
info = (NetworkInfo) msg.obj;
|
info = (NetworkInfo) msg.obj;
|
||||||
type = info.getType();
|
int type = info.getType();
|
||||||
updateNetworkSettings(mNetTrackers[type]);
|
updateNetworkSettings(mNetTrackers[type]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class InternalHandler extends Handler {
|
private class InternalHandler extends Handler {
|
||||||
public InternalHandler(Looper looper) {
|
public InternalHandler(Looper looper) {
|
||||||
@@ -3574,21 +3666,59 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
|
|
||||||
private boolean isMobileDataStateTrackerReady() {
|
private boolean isMobileDataStateTrackerReady() {
|
||||||
MobileDataStateTracker mdst =
|
MobileDataStateTracker mdst =
|
||||||
(MobileDataStateTracker) mNetTrackers[ConnectivityManager.TYPE_MOBILE];
|
(MobileDataStateTracker) mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI];
|
||||||
return (mdst != null) && (mdst.isReady());
|
return (mdst != null) && (mdst.isReady());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ResultReceiver resultCode for checkMobileProvisioning (CMP_RESULT_CODE)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No connection was possible to the network.
|
||||||
|
*/
|
||||||
|
public static final int CMP_RESULT_CODE_NO_CONNECTION = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A connection was made to the internet, all is well.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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
|
@Override
|
||||||
public int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs,
|
public int checkMobileProvisioning(int suggestedTimeOutMs) {
|
||||||
final ResultReceiver resultReceiver) {
|
int timeOutMs = -1;
|
||||||
log("checkMobileProvisioning: E sendNotification=" + sendNotification
|
if (DBG) log("checkMobileProvisioning: E suggestedTimeOutMs=" + suggestedTimeOutMs);
|
||||||
+ " suggestedTimeOutMs=" + suggestedTimeOutMs
|
enforceConnectivityInternalPermission();
|
||||||
+ " resultReceiver=" + resultReceiver);
|
|
||||||
enforceChangePermission();
|
|
||||||
|
|
||||||
mFirstProvisioningCheckStarted = true;
|
final long token = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
int timeOutMs = suggestedTimeOutMs;
|
timeOutMs = suggestedTimeOutMs;
|
||||||
if (suggestedTimeOutMs > CheckMp.MAX_TIMEOUT_MS) {
|
if (suggestedTimeOutMs > CheckMp.MAX_TIMEOUT_MS) {
|
||||||
timeOutMs = CheckMp.MAX_TIMEOUT_MS;
|
timeOutMs = CheckMp.MAX_TIMEOUT_MS;
|
||||||
}
|
}
|
||||||
@@ -3596,54 +3726,91 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
// Check that mobile networks are supported
|
// Check that mobile networks are supported
|
||||||
if (!isNetworkSupported(ConnectivityManager.TYPE_MOBILE)
|
if (!isNetworkSupported(ConnectivityManager.TYPE_MOBILE)
|
||||||
|| !isNetworkSupported(ConnectivityManager.TYPE_MOBILE_HIPRI)) {
|
|| !isNetworkSupported(ConnectivityManager.TYPE_MOBILE_HIPRI)) {
|
||||||
log("checkMobileProvisioning: X no mobile network");
|
if (DBG) log("checkMobileProvisioning: X no mobile network");
|
||||||
if (resultReceiver != null) {
|
return timeOutMs;
|
||||||
resultReceiver.send(ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION, null);
|
}
|
||||||
}
|
|
||||||
|
// 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;
|
return timeOutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
final long token = Binder.clearCallingIdentity();
|
|
||||||
try {
|
|
||||||
CheckMp checkMp = new CheckMp(mContext, this);
|
CheckMp checkMp = new CheckMp(mContext, this);
|
||||||
CheckMp.CallBack cb = new CheckMp.CallBack() {
|
CheckMp.CallBack cb = new CheckMp.CallBack() {
|
||||||
@Override
|
@Override
|
||||||
void onComplete(Integer result) {
|
void onComplete(Integer result) {
|
||||||
log("CheckMp.onComplete: result=" + result);
|
if (DBG) log("CheckMp.onComplete: result=" + result);
|
||||||
if (resultReceiver != null) {
|
|
||||||
log("CheckMp.onComplete: send result");
|
|
||||||
resultReceiver.send(result, null);
|
|
||||||
}
|
|
||||||
NetworkInfo ni =
|
NetworkInfo ni =
|
||||||
mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI].getNetworkInfo();
|
mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI].getNetworkInfo();
|
||||||
switch(result) {
|
switch(result) {
|
||||||
case ConnectivityManager.CMP_RESULT_CODE_CONNECTABLE:
|
case CMP_RESULT_CODE_CONNECTABLE:
|
||||||
case ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION: {
|
case CMP_RESULT_CODE_NO_CONNECTION: {
|
||||||
log("CheckMp.onComplete: ignore, connected or no connection");
|
if (DBG) log("CheckMp.onComplete: ignore, connected or no connection");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ConnectivityManager.CMP_RESULT_CODE_REDIRECTED: {
|
case CMP_RESULT_CODE_REDIRECTED: {
|
||||||
log("CheckMp.onComplete: warm sim");
|
if (DBG) log("CheckMp.onComplete: warm sim");
|
||||||
String url = getMobileProvisioningUrl();
|
String url = getMobileProvisioningUrl();
|
||||||
if (TextUtils.isEmpty(url)) {
|
if (TextUtils.isEmpty(url)) {
|
||||||
url = getMobileRedirectedProvisioningUrl();
|
url = getMobileRedirectedProvisioningUrl();
|
||||||
}
|
}
|
||||||
if (TextUtils.isEmpty(url) == false) {
|
if (TextUtils.isEmpty(url) == false) {
|
||||||
log("CheckMp.onComplete: warm sim (redirected), url=" + url);
|
if (DBG) log("CheckMp.onComplete: warm (redirected), url=" + url);
|
||||||
setNotificationVisible(true, ni, url);
|
setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(),
|
||||||
|
url);
|
||||||
} else {
|
} else {
|
||||||
log("CheckMp.onComplete: warm sim (redirected), no url");
|
if (DBG) log("CheckMp.onComplete: warm (redirected), no url");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ConnectivityManager.CMP_RESULT_CODE_NO_DNS:
|
case CMP_RESULT_CODE_NO_DNS:
|
||||||
case ConnectivityManager.CMP_RESULT_CODE_NO_TCP_CONNECTION: {
|
case CMP_RESULT_CODE_NO_TCP_CONNECTION: {
|
||||||
String url = getMobileProvisioningUrl();
|
String url = getMobileProvisioningUrl();
|
||||||
if (TextUtils.isEmpty(url) == false) {
|
if (TextUtils.isEmpty(url) == false) {
|
||||||
log("CheckMp.onComplete: warm sim (no dns/tcp), url=" + url);
|
if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), url=" + url);
|
||||||
setNotificationVisible(true, ni, url);
|
setProvNotificationVisible(true, ni.getType(), ni.getExtraInfo(),
|
||||||
|
url);
|
||||||
} else {
|
} else {
|
||||||
log("CheckMp.onComplete: warm sim (no dns/tcp), no url");
|
if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), no url");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3652,16 +3819,16 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mIsCheckingMobileProvisioning.set(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
CheckMp.Params params =
|
CheckMp.Params params =
|
||||||
new CheckMp.Params(checkMp.getDefaultUrl(), timeOutMs, cb);
|
new CheckMp.Params(checkMp.getDefaultUrl(), timeOutMs, cb);
|
||||||
log("checkMobileProvisioning: params=" + params);
|
if (DBG) log("checkMobileProvisioning: params=" + params);
|
||||||
setNotificationVisible(false, null, null);
|
|
||||||
checkMp.execute(params);
|
checkMp.execute(params);
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(token);
|
Binder.restoreCallingIdentity(token);
|
||||||
log("checkMobileProvisioning: X");
|
if (DBG) log("checkMobileProvisioning: X");
|
||||||
}
|
}
|
||||||
return timeOutMs;
|
return timeOutMs;
|
||||||
}
|
}
|
||||||
@@ -3733,14 +3900,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
* a known address that fetches the data we expect.
|
* a known address that fetches the data we expect.
|
||||||
*/
|
*/
|
||||||
private synchronized Integer isMobileOk(Params params) {
|
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);
|
Uri orgUri = Uri.parse(params.mUrl);
|
||||||
Random rand = new Random();
|
Random rand = new Random();
|
||||||
mParams = params;
|
mParams = params;
|
||||||
|
|
||||||
if (mCs.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
|
if (mCs.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
|
||||||
log("isMobileOk: not mobile capable");
|
log("isMobileOk: not mobile capable");
|
||||||
result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
|
result = CMP_RESULT_CODE_NO_CONNECTION;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3776,7 +3943,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (VDBG) log("isMobileOk: hipri not started yet");
|
if (VDBG) log("isMobileOk: hipri not started yet");
|
||||||
result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
|
result = CMP_RESULT_CODE_NO_CONNECTION;
|
||||||
sleep(1);
|
sleep(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3789,15 +3956,26 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
NetworkInfo.State state = mCs
|
NetworkInfo.State state = mCs
|
||||||
.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
|
.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI).getState();
|
||||||
if (state != NetworkInfo.State.CONNECTED) {
|
if (state != NetworkInfo.State.CONNECTED) {
|
||||||
if (VDBG) {
|
if (true/*VDBG*/) {
|
||||||
log("isMobileOk: not connected ni=" +
|
log("isMobileOk: not connected ni=" +
|
||||||
mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
|
mCs.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_HIPRI));
|
||||||
}
|
}
|
||||||
sleep(1);
|
sleep(1);
|
||||||
result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
|
result = CMP_RESULT_CODE_NO_CONNECTION;
|
||||||
continue;
|
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
|
// Get of the addresses associated with the url host. We need to use the
|
||||||
// address otherwise HttpURLConnection object will use the name to get
|
// address otherwise HttpURLConnection object will use the name to get
|
||||||
// the addresses and is will try every address but that will bypass the
|
// the addresses and is will try every address but that will bypass the
|
||||||
@@ -3808,7 +3986,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
addresses = InetAddress.getAllByName(orgUri.getHost());
|
addresses = InetAddress.getAllByName(orgUri.getHost());
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
log("isMobileOk: UnknownHostException");
|
log("isMobileOk: UnknownHostException");
|
||||||
result = ConnectivityManager.CMP_RESULT_CODE_NO_DNS;
|
result = CMP_RESULT_CODE_NO_DNS;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
log("isMobileOk: addresses=" + inetAddressesToString(addresses));
|
log("isMobileOk: addresses=" + inetAddressesToString(addresses));
|
||||||
@@ -3873,9 +4051,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
urlConn.setRequestProperty("Connection", "close");
|
urlConn.setRequestProperty("Connection", "close");
|
||||||
int responseCode = urlConn.getResponseCode();
|
int responseCode = urlConn.getResponseCode();
|
||||||
if (responseCode == 204) {
|
if (responseCode == 204) {
|
||||||
result = ConnectivityManager.CMP_RESULT_CODE_CONNECTABLE;
|
result = CMP_RESULT_CODE_CONNECTABLE;
|
||||||
} else {
|
} else {
|
||||||
result = ConnectivityManager.CMP_RESULT_CODE_REDIRECTED;
|
result = CMP_RESULT_CODE_REDIRECTED;
|
||||||
}
|
}
|
||||||
log("isMobileOk: connected responseCode=" + responseCode);
|
log("isMobileOk: connected responseCode=" + responseCode);
|
||||||
urlConn.disconnect();
|
urlConn.disconnect();
|
||||||
@@ -3889,7 +4067,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = ConnectivityManager.CMP_RESULT_CODE_NO_TCP_CONNECTION;
|
result = CMP_RESULT_CODE_NO_TCP_CONNECTION;
|
||||||
log("isMobileOk: loops|timed out");
|
log("isMobileOk: loops|timed out");
|
||||||
return result;
|
return result;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -3903,6 +4081,23 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
mCs.setEnableFailFastMobileData(DctConstants.DISABLED);
|
mCs.setEnableFailFastMobileData(DctConstants.DISABLED);
|
||||||
mCs.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
|
mCs.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
|
||||||
Phone.FEATURE_ENABLE_HIPRI);
|
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);
|
log("isMobileOk: X result=" + result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -3982,10 +4177,55 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
|
// 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 void setNotificationVisible(boolean visible, NetworkInfo networkInfo, String url) {
|
private BroadcastReceiver mProvisioningReceiver = new BroadcastReceiver() {
|
||||||
log("setNotificationVisible: E visible=" + visible + " ni=" + networkInfo + " url=" + url);
|
@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 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();
|
Resources r = Resources.getSystem();
|
||||||
NotificationManager notificationManager = (NotificationManager) mContext
|
NotificationManager notificationManager = (NotificationManager) mContext
|
||||||
@@ -3995,50 +4235,64 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
CharSequence title;
|
CharSequence title;
|
||||||
CharSequence details;
|
CharSequence details;
|
||||||
int icon;
|
int icon;
|
||||||
switch (networkInfo.getType()) {
|
Intent intent;
|
||||||
|
Notification notification = new Notification();
|
||||||
|
switch (networkType) {
|
||||||
case ConnectivityManager.TYPE_WIFI:
|
case ConnectivityManager.TYPE_WIFI:
|
||||||
log("setNotificationVisible: TYPE_WIFI");
|
|
||||||
title = r.getString(R.string.wifi_available_sign_in, 0);
|
title = r.getString(R.string.wifi_available_sign_in, 0);
|
||||||
details = r.getString(R.string.network_available_sign_in_detailed,
|
details = r.getString(R.string.network_available_sign_in_detailed,
|
||||||
networkInfo.getExtraInfo());
|
extraInfo);
|
||||||
icon = R.drawable.stat_notify_wifi_in_range;
|
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;
|
break;
|
||||||
case ConnectivityManager.TYPE_MOBILE:
|
case ConnectivityManager.TYPE_MOBILE:
|
||||||
case ConnectivityManager.TYPE_MOBILE_HIPRI:
|
case ConnectivityManager.TYPE_MOBILE_HIPRI:
|
||||||
log("setNotificationVisible: TYPE_MOBILE|HIPRI");
|
|
||||||
title = r.getString(R.string.network_available_sign_in, 0);
|
title = r.getString(R.string.network_available_sign_in, 0);
|
||||||
// TODO: Change this to pull from NetworkInfo once a printable
|
// TODO: Change this to pull from NetworkInfo once a printable
|
||||||
// name has been added to it
|
// name has been added to it
|
||||||
details = mTelephonyManager.getNetworkOperatorName();
|
details = mTelephonyManager.getNetworkOperatorName();
|
||||||
icon = R.drawable.stat_notify_rssi_in_range;
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
log("setNotificationVisible: other type=" + networkInfo.getType());
|
|
||||||
title = r.getString(R.string.network_available_sign_in, 0);
|
title = r.getString(R.string.network_available_sign_in, 0);
|
||||||
details = r.getString(R.string.network_available_sign_in_detailed,
|
details = r.getString(R.string.network_available_sign_in_detailed,
|
||||||
networkInfo.getExtraInfo());
|
extraInfo);
|
||||||
icon = R.drawable.stat_notify_rssi_in_range;
|
icon = R.drawable.stat_notify_rssi_in_range;
|
||||||
break;
|
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||||
}
|
|
||||||
|
|
||||||
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.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
|
||||||
Intent.FLAG_ACTIVITY_NEW_TASK);
|
Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
|
notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.when = 0;
|
||||||
|
notification.icon = icon;
|
||||||
|
notification.flags = Notification.FLAG_AUTO_CANCEL;
|
||||||
notification.tickerText = title;
|
notification.tickerText = title;
|
||||||
notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
|
notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
|
||||||
|
|
||||||
log("setNotificaitionVisible: notify notificaiton=" + notification);
|
try {
|
||||||
notificationManager.notify(NOTIFICATION_ID, 1, notification);
|
notificationManager.notify(NOTIFICATION_ID, 1, notification);
|
||||||
} else {
|
} catch (NullPointerException npe) {
|
||||||
log("setNotificaitionVisible: cancel");
|
loge("setNotificaitionVisible: visible notificationManager npe=" + npe);
|
||||||
notificationManager.cancel(NOTIFICATION_ID, 1);
|
npe.printStackTrace();
|
||||||
}
|
}
|
||||||
log("setNotificationVisible: X visible=" + visible + " ni=" + networkInfo + " url=" + url);
|
} else {
|
||||||
|
try {
|
||||||
|
notificationManager.cancel(NOTIFICATION_ID, 1);
|
||||||
|
} catch (NullPointerException npe) {
|
||||||
|
loge("setNotificaitionVisible: cancel notificationManager npe=" + npe);
|
||||||
|
npe.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mIsNotificationVisible = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Location to an updatable file listing carrier provisioning urls.
|
/** Location to an updatable file listing carrier provisioning urls.
|
||||||
@@ -4166,4 +4420,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
|
|||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProvisioningNotificationVisible(boolean visible, int networkType,
|
||||||
|
String extraInfo, String url) {
|
||||||
|
enforceConnectivityInternalPermission();
|
||||||
|
setProvNotificationVisible(visible, networkType, extraInfo, url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,8 @@ public class DctConstants {
|
|||||||
public static final int EVENT_DISCONNECT_DC_RETRYING = BASE + 34;
|
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 EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35;
|
||||||
public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 36;
|
public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 36;
|
||||||
|
public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 37;
|
||||||
|
public static final int CMD_IS_PROVISIONING_APN = BASE + 38;
|
||||||
|
|
||||||
/***** Constants *****/
|
/***** Constants *****/
|
||||||
|
|
||||||
@@ -112,4 +114,5 @@ public class DctConstants {
|
|||||||
public static final int ENABLED = 1;
|
public static final int ENABLED = 1;
|
||||||
|
|
||||||
public static final String APN_TYPE_KEY = "apnType";
|
public static final String APN_TYPE_KEY = "apnType";
|
||||||
|
public static final String PROVISIONING_URL_KEY = "provisioningUrl";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,6 +145,29 @@ public class TelephonyIntents {
|
|||||||
public static final String ACTION_ANY_DATA_CONNECTION_STATE_CHANGED
|
public static final String ACTION_ANY_DATA_CONNECTION_STATE_CHANGED
|
||||||
= "android.intent.action.ANY_DATA_STATE";
|
= "android.intent.action.ANY_DATA_STATE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast Action: Occurs when a data connection connects to a provisioning apn
|
||||||
|
* and is broadcast by the low level data connection code.
|
||||||
|
* The intent will have the following extra values:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li><em>apn</em> - A string that is the APN associated with this
|
||||||
|
* connection.</li>
|
||||||
|
* <li><em>apnType</em> - A string array of APN types associated with
|
||||||
|
* this connection. The APN type <code>"*"</code> is a special
|
||||||
|
* type that means this APN services all types.</li>
|
||||||
|
* <li><em>linkProperties</em> - The <code>LinkProperties</code> for this APN</li>
|
||||||
|
* <li><em>linkCapabilities</em> - The <code>linkCapabilities</code> for this APN</li>
|
||||||
|
* <li><em>iface</em> - A string that is the name of the interface</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p class="note">
|
||||||
|
* Requires the READ_PHONE_STATE permission.
|
||||||
|
*
|
||||||
|
* <p class="note">This is a protected intent that can only be sent
|
||||||
|
* by the system.
|
||||||
|
*/
|
||||||
|
public static final String ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN
|
||||||
|
= "android.intent.action.DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcast Action: An attempt to establish a data connection has failed.
|
* Broadcast Action: An attempt to establish a data connection has failed.
|
||||||
|
|||||||
Reference in New Issue
Block a user