Merge "Add subId as parameter for get/setDataEnabled." into lmp-mr1-dev
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 7d5db85..fa87c80 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -101,7 +101,7 @@
/**
* Identical to {@link #CONNECTIVITY_ACTION} broadcast, but sent without any
- * historic {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}.
+ * applicable {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}.
*
* @hide
*/
@@ -429,6 +429,18 @@
public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
/**
+ * Default value for {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY} in
+ * milliseconds. This was introduced because IPv6 routes seem to take a
+ * moment to settle - trying network activity before the routes are adjusted
+ * can lead to packets using the wrong interface or having the wrong IP address.
+ * This delay is a bit crude, but in the future hopefully we will have kernel
+ * notifications letting us know when it's safe to use the new network.
+ *
+ * @hide
+ */
+ public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000;
+
+ /**
* @hide
*/
public final static int REQUEST_ID_UNSET = 0;
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 8021210..d9921a6 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -68,9 +68,6 @@
boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
- /** Policy control over specific {@link NetworkStateTracker}. */
- void setPolicyDataEnable(int networkType, boolean enabled);
-
int tether(String iface);
int untether(String iface);
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 5d2a43d..b92c9e3 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -20,15 +20,18 @@
import android.os.Parcelable;
/**
- * A grab-bag of information (metadata, policies, properties, etc) about a {@link Network}.
+ * A grab-bag of information (metadata, policies, properties, etc) about a
+ * {@link Network}. Since this contains PII, it should not be sent outside the
+ * system.
*
* @hide
*/
public class NetworkMisc implements Parcelable {
/**
- * If the {@link Network} is a VPN, whether apps are allowed to bypass the VPN. This is set by
- * a {@link VpnService} and used by {@link ConnectivityService} when creating a VPN.
+ * If the {@link Network} is a VPN, whether apps are allowed to bypass the
+ * VPN. This is set by a {@link VpnService} and used by
+ * {@link ConnectivityManager} when creating a VPN.
*/
public boolean allowBypass;
@@ -41,6 +44,11 @@
*/
public boolean explicitlySelected;
+ /**
+ * For mobile networks, this is the subscriber ID (such as IMSI).
+ */
+ public String subscriberId;
+
public NetworkMisc() {
}
@@ -48,6 +56,7 @@
if (nm != null) {
allowBypass = nm.allowBypass;
explicitlySelected = nm.explicitlySelected;
+ subscriberId = nm.subscriberId;
}
}
@@ -60,6 +69,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(allowBypass ? 1 : 0);
out.writeInt(explicitlySelected ? 1 : 0);
+ out.writeString(subscriberId);
}
public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -68,6 +78,7 @@
NetworkMisc networkMisc = new NetworkMisc();
networkMisc.allowBypass = in.readInt() != 0;
networkMisc.explicitlySelected = in.readInt() != 0;
+ networkMisc.subscriberId = in.readString();
return networkMisc;
}
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index d26c70d..933287f 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -30,16 +30,10 @@
public final LinkProperties linkProperties;
public final NetworkCapabilities networkCapabilities;
public final Network network;
- /** Currently only used by testing. */
public final String subscriberId;
public final String networkId;
public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
- NetworkCapabilities networkCapabilities, Network network) {
- this(networkInfo, linkProperties, networkCapabilities, network, null, null);
- }
-
- public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
NetworkCapabilities networkCapabilities, Network network, String subscriberId,
String networkId) {
this.networkInfo = networkInfo;
@@ -85,5 +79,4 @@
return new NetworkState[size];
}
};
-
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0f1ed0a..a86d564 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -20,22 +20,8 @@
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
-import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
-import static android.net.ConnectivityManager.TYPE_DUMMY;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
-import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
-import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
-import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
-import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
-import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
-import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
import static android.net.ConnectivityManager.TYPE_NONE;
-import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_VPN;
-import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
@@ -45,11 +31,9 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
@@ -62,7 +46,6 @@
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
-import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LinkProperties.CompareResult;
import android.net.MobileDataStateTracker;
@@ -72,7 +55,6 @@
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkFactory;
import android.net.NetworkMisc;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
@@ -80,16 +62,12 @@
import android.net.NetworkStateTracker;
import android.net.NetworkUtils;
import android.net.Proxy;
-import android.net.ProxyDataTracker;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.SamplingDataTracker;
import android.net.UidRange;
import android.net.Uri;
-import android.net.wimax.WimaxManagerConstants;
-import android.os.AsyncTask;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Handler;
@@ -103,7 +81,6 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -126,9 +103,6 @@
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
@@ -146,8 +120,6 @@
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
-import dalvik.system.DexClassLoader;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -157,30 +129,20 @@
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.reflect.Constructor;
-import java.net.HttpURLConnection;
import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLSession;
-
/**
* @hide
*/
@@ -215,6 +177,10 @@
private static final int SAMPLE_INTERVAL_ELAPSED_REQUEST_CODE = 0;
+ // How long to delay to removal of a pending intent based request.
+ // See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
+ private final int mReleasePendingIntentDelayMs;
+
private PendingIntent mSampleIntervalElapsedIntent;
// Set network sampling interval at 12 minutes, this way, even if the timers get
@@ -265,6 +231,7 @@
private static ConnectivityService sServiceInstance;
private INetworkManagementService mNetd;
+ private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyManager;
private String mCurrentTcpBufferSizes;
@@ -272,6 +239,26 @@
private static final int ENABLED = 1;
private static final int DISABLED = 0;
+ // Arguments to rematchNetworkAndRequests()
+ private enum NascentState {
+ // Indicates a network was just validated for the first time. If the network is found to
+ // be unwanted (i.e. not satisfy any NetworkRequests) it is torn down.
+ JUST_VALIDATED,
+ // Indicates a network was not validated for the first time immediately prior to this call.
+ NOT_JUST_VALIDATED
+ };
+ private enum ReapUnvalidatedNetworks {
+ // Tear down unvalidated networks that have no chance (i.e. even if validated) of becoming
+ // the highest scoring network satisfying a NetworkRequest. This should be passed when it's
+ // known that there may be unvalidated networks that could potentially be reaped, and when
+ // all networks have been rematched against all NetworkRequests.
+ REAP,
+ // Don't reap unvalidated networks. This should be passed when it's known that there are
+ // no unvalidated networks that could potentially be reaped, and when some networks have
+ // not yet been rematched against all NetworkRequests.
+ DONT_REAP
+ };
+
/**
* used internally to change our mobile data enabled flag
*/
@@ -303,12 +290,6 @@
private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = 11;
/**
- * Used internally to
- * {@link NetworkStateTracker#setPolicyDataEnable(boolean)}.
- */
- private static final int EVENT_SET_POLICY_DATA_ENABLE = 12;
-
- /**
* Used internally to disable fail fast of mobile data
*/
private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 14;
@@ -645,8 +626,12 @@
loge("Error setting defaultDns using " + dns);
}
+ mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
+
mContext = checkNotNull(context, "missing Context");
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
+ mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
mKeyStore = KeyStore.getInstance();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -797,6 +782,17 @@
throw new IllegalStateException("No free netIds");
}
+ private int getConnectivityChangeDelay() {
+ final ContentResolver cr = mContext.getContentResolver();
+
+ /** Check system properties for the default value then use secure settings value, if any. */
+ int defaultDelay = SystemProperties.getInt(
+ "conn." + Settings.Global.CONNECTIVITY_CHANGE_DELAY,
+ ConnectivityManager.CONNECTIVITY_CHANGE_DELAY_DEFAULT);
+ return Settings.Global.getInt(cr, Settings.Global.CONNECTIVITY_CHANGE_DELAY,
+ defaultDelay);
+ }
+
private boolean teardown(NetworkStateTracker netTracker) {
if (netTracker.teardown()) {
netTracker.setTeardownRequested(true);
@@ -811,6 +807,7 @@
LinkProperties lp = null;
NetworkCapabilities nc = null;
Network network = null;
+ String subscriberId = null;
if (mLegacyTypeTracker.isTypeSupported(networkType)) {
NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
@@ -820,6 +817,7 @@
lp = new LinkProperties(nai.linkProperties);
nc = new NetworkCapabilities(nai.networkCapabilities);
network = new Network(nai.network);
+ subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
}
info.setType(networkType);
} else {
@@ -833,7 +831,7 @@
info = getFilteredNetworkInfo(info, lp, uid);
}
- return new NetworkState(info, lp, nc, network);
+ return new NetworkState(info, lp, nc, network, subscriberId, null);
}
private NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
@@ -850,6 +848,7 @@
LinkProperties lp = null;
NetworkCapabilities nc = null;
Network network = null;
+ String subscriberId = null;
NetworkAgentInfo nai = mNetworkForRequestId.get(mDefaultRequest.requestId);
@@ -881,10 +880,11 @@
lp = new LinkProperties(nai.linkProperties);
nc = new NetworkCapabilities(nai.networkCapabilities);
network = new Network(nai.network);
+ subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
}
}
- return new NetworkState(info, lp, nc, network);
+ return new NetworkState(info, lp, nc, network, subscriberId, null);
}
/**
@@ -1181,14 +1181,19 @@
@Override
public NetworkState[] getAllNetworkState() {
- enforceAccessPermission();
- final int uid = Binder.getCallingUid();
+ // Require internal since we're handing out IMSI details
+ enforceConnectivityInternalPermission();
+
final ArrayList<NetworkState> result = Lists.newArrayList();
- for (int networkType = 0; networkType <= ConnectivityManager.MAX_NETWORK_TYPE;
- networkType++) {
- NetworkState state = getFilteredNetworkState(networkType, uid);
- if (state.networkInfo != null) {
- result.add(state);
+ for (Network network : getAllNetworks()) {
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai != null) {
+ synchronized (nai) {
+ final String subscriberId = (nai.networkMisc != null)
+ ? nai.networkMisc.subscriberId : null;
+ result.add(new NetworkState(nai.networkInfo, nai.linkProperties,
+ nai.networkCapabilities, network, subscriberId, null));
+ }
}
}
return result.toArray(new NetworkState[result.size()]);
@@ -1413,25 +1418,6 @@
}
};
- @Override
- public void setPolicyDataEnable(int networkType, boolean enabled) {
- // only someone like NPMS should only be calling us
- mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_SET_POLICY_DATA_ENABLE, networkType, (enabled ? ENABLED : DISABLED)));
- }
-
- private void handleSetPolicyDataEnable(int networkType, boolean enabled) {
- // TODO - handle this passing to factories
-// if (isNetworkTypeValid(networkType)) {
-// final NetworkStateTracker tracker = mNetTrackers[networkType];
-// if (tracker != null) {
-// tracker.setPolicyDataEnable(enabled);
-// }
-// }
- }
-
private void enforceInternetPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET,
@@ -1468,6 +1454,11 @@
sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
}
+ private void sendConnectedBroadcastDelayed(NetworkInfo info, int delayMs) {
+ sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE);
+ sendGeneralBroadcastDelayed(info, CONNECTIVITY_ACTION, delayMs);
+ }
+
private void sendInetConditionBroadcast(NetworkInfo info) {
sendGeneralBroadcast(info, ConnectivityManager.INET_CONDITION_ACTION);
}
@@ -1499,6 +1490,10 @@
sendStickyBroadcast(makeGeneralIntent(info, bcastType));
}
+ private void sendGeneralBroadcastDelayed(NetworkInfo info, String bcastType, int delayMs) {
+ sendStickyBroadcastDelayed(makeGeneralIntent(info, bcastType), delayMs);
+ }
+
private void sendDataActivityBroadcast(int deviceType, boolean active, long tsNanos) {
Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
@@ -1524,6 +1519,17 @@
}
final long ident = Binder.clearCallingIdentity();
+ if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+ final IBatteryStats bs = BatteryStatsService.getService();
+ try {
+ NetworkInfo ni = intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ bs.noteConnectivityChanged(intent.getIntExtra(
+ ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_NONE),
+ ni != null ? ni.getState().toString() : "?");
+ } catch (RemoteException e) {
+ }
+ }
try {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
} finally {
@@ -1532,6 +1538,19 @@
}
}
+ private void sendStickyBroadcastDelayed(Intent intent, int delayMs) {
+ if (delayMs <= 0) {
+ sendStickyBroadcast(intent);
+ } else {
+ if (VDBG) {
+ log("sendStickyBroadcastDelayed: delayMs=" + delayMs + ", action="
+ + intent.getAction());
+ }
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ EVENT_SEND_STICKY_BROADCAST_INTENT, intent), delayMs);
+ }
+ }
+
void systemReady() {
// start network sampling ..
Intent intent = new Intent(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED);
@@ -1970,12 +1989,11 @@
boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
if (valid) {
if (DBG) log("Validated " + nai.name());
- final boolean previouslyValidated = nai.validated;
- final int previousScore = nai.getCurrentScore();
- nai.validated = true;
- rematchNetworkAndRequests(nai, !previouslyValidated);
- // If score has changed, rebroadcast to NetworkFactories. b/17726566
- if (nai.getCurrentScore() != previousScore) {
+ if (!nai.validated) {
+ nai.validated = true;
+ rematchNetworkAndRequests(nai, NascentState.JUST_VALIDATED,
+ ReapUnvalidatedNetworks.REAP);
+ // If score has changed, rebroadcast to NetworkFactories. b/17726566
sendUpdatedScoreToFactories(nai);
}
}
@@ -2150,6 +2168,7 @@
if (isDefaultNetwork(nai)) {
mDefaultInetConditionPublished = 0;
}
+ notifyIfacesChanged();
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
@@ -2194,7 +2213,8 @@
}
for (NetworkAgentInfo networkToActivate : toActivate) {
unlinger(networkToActivate);
- rematchNetworkAndRequests(networkToActivate, false);
+ rematchNetworkAndRequests(networkToActivate, NascentState.NOT_JUST_VALIDATED,
+ ReapUnvalidatedNetworks.DONT_REAP);
}
}
}
@@ -2385,12 +2405,6 @@
sendStickyBroadcast(intent);
break;
}
- case EVENT_SET_POLICY_DATA_ENABLE: {
- final int networkType = msg.arg1;
- final boolean enabled = msg.arg2 == ENABLED;
- handleSetPolicyDataEnable(networkType, enabled);
- break;
- }
case EVENT_ENABLE_FAIL_FAST_MOBILE_DATA: {
int tag = mEnableFailFastMobileDataTag.get();
if (msg.arg1 == tag) {
@@ -3376,6 +3390,7 @@
final NetworkRequest request;
final PendingIntent mPendingIntent;
+ boolean mPendingIntentSent;
private final IBinder mBinder;
final int mPid;
final int mUid;
@@ -3497,6 +3512,12 @@
return networkRequest;
}
+ private void releasePendingNetworkRequestWithDelay(PendingIntent operation) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
+ getCallingUid(), 0, operation), mReleasePendingIntentDelayMs);
+ }
+
@Override
public void releasePendingNetworkRequest(PendingIntent operation) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
@@ -3642,6 +3663,7 @@
if (isDefaultNetwork(networkAgent)) handleApplyDefaultProxy(newLp.getHttpProxy());
// TODO - move this check to cover the whole function
if (!Objects.equals(newLp, oldLp)) {
+ notifyIfacesChanged();
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
}
}
@@ -3779,7 +3801,6 @@
mNumDnsEntries = last;
}
-
private void updateCapabilities(NetworkAgentInfo networkAgent,
NetworkCapabilities networkCapabilities) {
if (!Objects.equals(networkAgent.networkCapabilities, networkCapabilities)) {
@@ -3810,10 +3831,11 @@
private void sendPendingIntentForRequest(NetworkRequestInfo nri, NetworkAgentInfo networkAgent,
int notificationType) {
- if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE) {
+ if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
Intent intent = new Intent();
intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.request);
+ nri.mPendingIntentSent = true;
sendIntent(nri.mPendingIntent, intent);
}
// else not handled
@@ -3837,7 +3859,9 @@
String resultData, Bundle resultExtras) {
if (DBG) log("Finished sending " + pendingIntent);
mPendingIntentWakeLock.release();
- releasePendingNetworkRequest(pendingIntent);
+ // Release with a delay so the receiving client has an opportunity to put in its
+ // own request.
+ releasePendingNetworkRequestWithDelay(pendingIntent);
}
private void callCallbackForRequest(NetworkRequestInfo nri,
@@ -3920,15 +3944,16 @@
// satisfied by newNetwork, and reassigns to newNetwork
// any such requests for which newNetwork is the best.
//
- // - Lingers any Networks that as a result are no longer
+ // - Lingers any validated Networks that as a result are no longer
// needed. A network is needed if it is the best network for
// one or more NetworkRequests, or if it is a VPN.
//
// - Tears down newNetwork if it just became validated
- // (i.e. nascent==true) but turns out to be unneeded.
- // Does not tear down newNetwork if it is unvalidated,
- // because future validation may improve newNetwork's
- // score enough that it is needed.
+ // (i.e. nascent==JUST_VALIDATED) but turns out to be unneeded.
+ //
+ // - If reapUnvalidatedNetworks==REAP, tears down unvalidated
+ // networks that have no chance (i.e. even if validated)
+ // of becoming the highest scoring network.
//
// NOTE: This function only adds NetworkRequests that "newNetwork" could satisfy,
// it does not remove NetworkRequests that other Networks could better satisfy.
@@ -3936,16 +3961,18 @@
// This function should be used when possible instead of {@code rematchAllNetworksAndRequests}
// as it performs better by a factor of the number of Networks.
//
+ // @param newNetwork is the network to be matched against NetworkRequests.
// @param nascent indicates if newNetwork just became validated, in which case it should be
- // torn down if unneeded. If nascent is false, no action is taken if newNetwork
- // is found to be unneeded by this call. Presumably, in this case, either:
- // - newNetwork is unvalidated (and left alive), or
- // - the NetworkRequests keeping newNetwork alive have been transitioned to
- // another higher scoring network by another call to rematchNetworkAndRequests()
- // and this other call also lingered newNetwork.
- private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, boolean nascent) {
+ // torn down if unneeded.
+ // @param reapUnvalidatedNetworks indicates if an additional pass over all networks should be
+ // performed to tear down unvalidated networks that have no chance (i.e. even if
+ // validated) of becoming the highest scoring network.
+ private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, NascentState nascent,
+ ReapUnvalidatedNetworks reapUnvalidatedNetworks) {
if (!newNetwork.created) return;
- if (nascent && !newNetwork.validated) loge("ERROR: nascent network not validated.");
+ if (nascent == NascentState.JUST_VALIDATED && !newNetwork.validated) {
+ loge("ERROR: nascent network not validated.");
+ }
boolean keep = newNetwork.isVPN();
boolean isNewDefault = false;
if (DBG) log("rematching " + newNetwork.name());
@@ -4092,17 +4119,44 @@
if (newNetwork.isVPN()) {
mLegacyTypeTracker.add(TYPE_VPN, newNetwork);
}
- } else if (nascent) {
+ } else if (nascent == NascentState.JUST_VALIDATED) {
// Only tear down newly validated networks here. Leave unvalidated to either become
- // validated (and get evaluated against peers, one losing here) or
- // NetworkMonitor reports a bad network and we tear it down then.
- // Networks that have been up for a while and are validated should be torn down via
- // the lingering process so communication on that network is given time to wrap up.
- // TODO: Could teardown unvalidated networks when their NetworkCapabilities
- // satisfy no NetworkRequests.
+ // validated (and get evaluated against peers, one losing here), or get reaped (see
+ // reapUnvalidatedNetworks) if they have no chance of becoming the highest scoring
+ // network. Networks that have been up for a while and are validated should be torn
+ // down via the lingering process so communication on that network is given time to
+ // wrap up.
if (DBG) log("Validated network turns out to be unwanted. Tear it down.");
teardownUnneededNetwork(newNetwork);
}
+ if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
+ for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ if (!nai.created || nai.validated || nai.isVPN()) continue;
+ boolean reap = true;
+ for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ // If this Network is already the highest scoring Network for a request, or if
+ // there is hope for it to become one if it validated, then don't reap it.
+ if (nri.isRequest && nai.satisfies(nri.request) &&
+ (nai.networkRequests.get(nri.request.requestId) != null ||
+ // Note that this catches two important cases:
+ // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
+ // is currently satisfying the request. This is desirable when
+ // cellular ends up validating but WiFi does not.
+ // 2. Unvalidated WiFi will not be reaped when validated cellular
+ // is currently satsifying the request. This is desirable when
+ // WiFi ends up validating and out scoring cellular.
+ mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
+ nai.getCurrentScoreAsValidated())) {
+ reap = false;
+ break;
+ }
+ }
+ if (reap) {
+ if (DBG) log("Reaping " + nai.name());
+ teardownUnneededNetwork(nai);
+ }
+ }
+ }
}
// Attempt to rematch all Networks with NetworkRequests. This may result in Networks
@@ -4123,10 +4177,17 @@
// can only add more NetworkRequests satisfied by "changed", and this is exactly what
// rematchNetworkAndRequests() handles.
if (changed != null && oldScore < changed.getCurrentScore()) {
- rematchNetworkAndRequests(changed, false);
+ rematchNetworkAndRequests(changed, NascentState.NOT_JUST_VALIDATED,
+ ReapUnvalidatedNetworks.REAP);
} else {
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- rematchNetworkAndRequests(nai, false);
+ for (Iterator i = mNetworkAgentInfos.values().iterator(); i.hasNext(); ) {
+ rematchNetworkAndRequests((NetworkAgentInfo)i.next(),
+ NascentState.NOT_JUST_VALIDATED,
+ // Only reap the last time through the loop. Reaping before all rematching
+ // is complete could incorrectly teardown a network that hasn't yet been
+ // rematched.
+ i.hasNext() ? ReapUnvalidatedNetworks.DONT_REAP
+ : ReapUnvalidatedNetworks.REAP);
}
}
}
@@ -4193,6 +4254,7 @@
}
networkAgent.created = true;
updateLinkProperties(networkAgent, null);
+ notifyIfacesChanged();
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
if (networkAgent.isVPN()) {
@@ -4208,7 +4270,8 @@
// TODO: support proxy per network.
}
// Consider network even though it is not yet validated.
- rematchNetworkAndRequests(networkAgent, false);
+ rematchNetworkAndRequests(networkAgent, NascentState.NOT_JUST_VALIDATED,
+ ReapUnvalidatedNetworks.REAP);
} else if (state == NetworkInfo.State.DISCONNECTED ||
state == NetworkInfo.State.SUSPENDED) {
networkAgent.asyncChannel.disconnect();
@@ -4268,7 +4331,7 @@
info.setType(type);
if (connected) {
info.setDetailedState(DetailedState.CONNECTED, null, info.getExtraInfo());
- sendConnectedBroadcast(info);
+ sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
} else {
info.setDetailedState(DetailedState.DISCONNECTED, null, info.getExtraInfo());
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
@@ -4299,9 +4362,10 @@
final Intent immediateIntent = new Intent(intent);
immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
sendStickyBroadcast(immediateIntent);
- sendStickyBroadcast(intent);
+ sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
if (newDefaultAgent != null) {
- sendConnectedBroadcast(newDefaultAgent.networkInfo);
+ sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo,
+ getConnectivityChangeDelay());
}
}
}
@@ -4334,6 +4398,16 @@
return "UNKNOWN";
}
+ /**
+ * Notify other system services that set of active ifaces has changed.
+ */
+ private void notifyIfacesChanged() {
+ try {
+ mStatsService.forceUpdateIfaces();
+ } catch (Exception ignored) {
+ }
+ }
+
@Override
public boolean addVpnAddress(String address, int prefixLength) {
throwIfLockdownEnabled();
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 6feeb7c..779a834 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -106,9 +106,7 @@
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
}
- // Get the current score for this Network. This may be modified from what the
- // NetworkAgent sent, as it has modifiers applied to it.
- public int getCurrentScore() {
+ private int getCurrentScore(boolean pretendValidated) {
// TODO: We may want to refactor this into a NetworkScore class that takes a base score from
// the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the
// score. The NetworkScore class would provide a nice place to centralize score constants
@@ -116,7 +114,7 @@
int score = currentScore;
- if (!validated) score -= UNVALIDATED_SCORE_PENALTY;
+ if (!validated && !pretendValidated) score -= UNVALIDATED_SCORE_PENALTY;
if (score < 0) score = 0;
if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE;
@@ -124,6 +122,18 @@
return score;
}
+ // Get the current score for this Network. This may be modified from what the
+ // NetworkAgent sent, as it has modifiers applied to it.
+ public int getCurrentScore() {
+ return getCurrentScore(false);
+ }
+
+ // Get the current score for this Network as if it was validated. This may be modified from
+ // what the NetworkAgent sent, as it has modifiers applied to it.
+ public int getCurrentScoreAsValidated() {
+ return getCurrentScore(true);
+ }
+
public void setCurrentScore(int newScore) {
currentScore = newScore;
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index f9a03fc..7383478 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -1023,7 +1023,7 @@
info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(iface);
- return new NetworkState(info, prop, null, null);
+ return new NetworkState(info, prop, null, null, null, null);
}
private NetworkStats buildEmptyStats() {