Add Multinetwork API

Change-Id: I3a9cef0d416db96d05098dd989ee3fef3b1e9274
(cherry picked from commit cc5e6afa1ba0bef099bcb21a64a36bc2bf7951db)
This commit is contained in:
Robert Greenwalt
2014-03-26 16:47:06 -07:00
parent be46b75c3b
commit f99b8393d6
4 changed files with 809 additions and 61 deletions

View File

@@ -13,26 +13,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.INetworkActivityListener;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Log;
import java.net.InetAddress;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.HashMap;
import com.android.internal.util.Protocol;
/**
* Class that answers queries about the state of network connectivity. It also
@@ -699,7 +708,25 @@ public class ConnectivityManager {
*/
public LinkProperties getLinkProperties(int networkType) {
try {
return mService.getLinkProperties(networkType);
return mService.getLinkPropertiesForType(networkType);
} catch (RemoteException e) {
return null;
}
}
/** {@hide} */
public LinkProperties getLinkProperties(Network network) {
try {
return mService.getLinkProperties(network);
} catch (RemoteException e) {
return null;
}
}
/** {@hide} */
public NetworkCapabilities getNetworkCapabilities(Network network) {
try {
return mService.getNetworkCapabilities(network);
} catch (RemoteException e) {
return null;
}
@@ -1302,6 +1329,22 @@ public class ConnectivityManager {
}
}
/**
* Report a problem network to the framework. This will cause the framework
* to evaluate the situation and try to fix any problems. Note that false
* may be subsequently ignored.
*
* @param network The Network the application was attempting to use or null
* to indicate the current default network.
* {@hide}
*/
public void reportBadNetwork(Network network) {
try {
mService.reportBadNetwork(network);
} catch (RemoteException e) {
}
}
/**
* Set a network-independent global http proxy. This is not normally what you want
* for typical HTTP proxies - they are general network dependent. However if you're
@@ -1599,15 +1642,27 @@ public class ConnectivityManager {
} catch (RemoteException e) { }
}
/** Interface for NetworkRequest callbacks {@hide} */
/**
* Interface for NetworkRequest callbacks. Used for notifications about network
* changes.
* @hide
*/
public static class NetworkCallbacks {
/** @hide */
public static final int PRECHECK = 1;
/** @hide */
public static final int AVAILABLE = 2;
/** @hide */
public static final int LOSING = 3;
/** @hide */
public static final int LOST = 4;
/** @hide */
public static final int UNAVAIL = 5;
/** @hide */
public static final int CAP_CHANGED = 6;
/** @hide */
public static final int PROP_CHANGED = 7;
/** @hide */
public static final int CANCELED = 8;
/**
@@ -1650,21 +1705,361 @@ public class ConnectivityManager {
* Called when the network the framework connected to for this request
* changes capabilities but still satisfies the stated need.
*/
public void onCapabilitiesChanged(NetworkRequest networkRequest, Network network,
public void onNetworkCapabilitiesChanged(NetworkRequest networkRequest, Network network,
NetworkCapabilities networkCapabilities) {}
/**
* Called when the network the framework connected to for this request
* changes properties.
* changes LinkProperties.
*/
public void onPropertiesChanged(NetworkRequest networkRequest, Network network,
public void onLinkPropertiesChanged(NetworkRequest networkRequest, Network network,
LinkProperties linkProperties) {}
/**
* Called when a CancelRequest call concludes and the registered callbacks will
* Called when a releaseNetworkRequest call concludes and the registered callbacks will
* no longer be used.
*/
public void onCanceled(NetworkRequest networkRequest) {}
public void onReleased(NetworkRequest networkRequest) {}
}
private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER;
/** @hide obj = pair(NetworkRequest, Network) */
public static final int CALLBACK_PRECHECK = BASE + 1;
/** @hide obj = pair(NetworkRequest, Network) */
public static final int CALLBACK_AVAILABLE = BASE + 2;
/** @hide obj = pair(NetworkRequest, Network), arg1 = ttl */
public static final int CALLBACK_LOSING = BASE + 3;
/** @hide obj = pair(NetworkRequest, Network) */
public static final int CALLBACK_LOST = BASE + 4;
/** @hide obj = NetworkRequest */
public static final int CALLBACK_UNAVAIL = BASE + 5;
/** @hide obj = pair(NetworkRequest, Network) */
public static final int CALLBACK_CAP_CHANGED = BASE + 6;
/** @hide obj = pair(NetworkRequest, Network) */
public static final int CALLBACK_IP_CHANGED = BASE + 7;
/** @hide obj = NetworkRequest */
public static final int CALLBACK_RELEASED = BASE + 8;
/** @hide */
public static final int CALLBACK_EXIT = BASE + 9;
private static class CallbackHandler extends Handler {
private final HashMap<NetworkRequest, NetworkCallbacks>mCallbackMap;
private final AtomicInteger mRefCount;
private static final String TAG = "ConnectivityManager.CallbackHandler";
private final ConnectivityManager mCm;
CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallbacks>callbackMap,
AtomicInteger refCount, ConnectivityManager cm) {
super(looper);
mCallbackMap = callbackMap;
mRefCount = refCount;
mCm = cm;
}
@Override
public void handleMessage(Message message) {
Log.d(TAG, "CM callback handler got msg " + message.what);
switch (message.what) {
case CALLBACK_PRECHECK: {
NetworkRequest request = getNetworkRequest(message);
NetworkCallbacks callbacks = getCallbacks(request);
if (callbacks != null) {
callbacks.onPreCheck(request, getNetwork(message));
} else {
Log.e(TAG, "callback not found for PRECHECK message");
}
break;
}
case CALLBACK_AVAILABLE: {
NetworkRequest request = getNetworkRequest(message);
NetworkCallbacks callbacks = getCallbacks(request);
if (callbacks != null) {
callbacks.onAvailable(request, getNetwork(message));
} else {
Log.e(TAG, "callback not found for AVAILABLE message");
}
break;
}
case CALLBACK_LOSING: {
NetworkRequest request = getNetworkRequest(message);
NetworkCallbacks callbacks = getCallbacks(request);
if (callbacks != null) {
callbacks.onLosing(request, getNetwork(message), message.arg1);
} else {
Log.e(TAG, "callback not found for LOSING message");
}
break;
}
case CALLBACK_LOST: {
NetworkRequest request = getNetworkRequest(message);
NetworkCallbacks callbacks = getCallbacks(request);
if (callbacks != null) {
callbacks.onLost(request, getNetwork(message));
} else {
Log.e(TAG, "callback not found for LOST message");
}
break;
}
case CALLBACK_UNAVAIL: {
NetworkRequest req = (NetworkRequest)message.obj;
NetworkCallbacks callbacks = null;
synchronized(mCallbackMap) {
callbacks = mCallbackMap.get(req);
}
if (callbacks != null) {
callbacks.onUnavailable(req);
} else {
Log.e(TAG, "callback not found for UNAVAIL message");
}
break;
}
case CALLBACK_CAP_CHANGED: {
NetworkRequest request = getNetworkRequest(message);
NetworkCallbacks callbacks = getCallbacks(request);
if (callbacks != null) {
Network network = getNetwork(message);
NetworkCapabilities cap = mCm.getNetworkCapabilities(network);
callbacks.onNetworkCapabilitiesChanged(request, network, cap);
} else {
Log.e(TAG, "callback not found for CHANGED message");
}
break;
}
case CALLBACK_IP_CHANGED: {
NetworkRequest request = getNetworkRequest(message);
NetworkCallbacks callbacks = getCallbacks(request);
if (callbacks != null) {
Network network = getNetwork(message);
LinkProperties lp = mCm.getLinkProperties(network);
callbacks.onLinkPropertiesChanged(request, network, lp);
} else {
Log.e(TAG, "callback not found for CHANGED message");
}
break;
}
case CALLBACK_RELEASED: {
NetworkRequest req = (NetworkRequest)message.obj;
NetworkCallbacks callbacks = null;
synchronized(mCallbackMap) {
callbacks = mCallbackMap.remove(req);
}
if (callbacks != null) {
callbacks.onReleased(req);
} else {
Log.e(TAG, "callback not found for CANCELED message");
}
synchronized(mRefCount) {
if (mRefCount.decrementAndGet() == 0) {
getLooper().quit();
}
}
break;
}
case CALLBACK_EXIT: {
Log.d(TAG, "Listener quiting");
getLooper().quit();
break;
}
}
}
private NetworkRequest getNetworkRequest(Message msg) {
return (NetworkRequest)(msg.obj);
}
private NetworkCallbacks getCallbacks(NetworkRequest req) {
synchronized(mCallbackMap) {
return mCallbackMap.get(req);
}
}
private Network getNetwork(Message msg) {
return new Network(msg.arg2);
}
private NetworkCallbacks removeCallbacks(Message msg) {
NetworkRequest req = (NetworkRequest)msg.obj;
synchronized(mCallbackMap) {
return mCallbackMap.remove(req);
}
}
}
private void addCallbackListener() {
synchronized(sCallbackRefCount) {
if (sCallbackRefCount.incrementAndGet() == 1) {
// TODO - switch this over to a ManagerThread or expire it when done
HandlerThread callbackThread = new HandlerThread("ConnectivityManager");
callbackThread.start();
sCallbackHandler = new CallbackHandler(callbackThread.getLooper(),
sNetworkCallbacks, sCallbackRefCount, this);
}
}
}
private void removeCallbackListener() {
synchronized(sCallbackRefCount) {
if (sCallbackRefCount.decrementAndGet() == 0) {
sCallbackHandler.obtainMessage(CALLBACK_EXIT).sendToTarget();
sCallbackHandler = null;
}
}
}
static final HashMap<NetworkRequest, NetworkCallbacks> sNetworkCallbacks =
new HashMap<NetworkRequest, NetworkCallbacks>();
static final AtomicInteger sCallbackRefCount = new AtomicInteger(0);
static CallbackHandler sCallbackHandler = null;
private final static int LISTEN = 1;
private final static int REQUEST = 2;
private NetworkRequest somethingForNetwork(NetworkCapabilities need,
NetworkCallbacks networkCallbacks, int timeoutSec, int action) {
NetworkRequest networkRequest = null;
if (networkCallbacks == null) throw new IllegalArgumentException("null NetworkCallbacks");
if (need == null) throw new IllegalArgumentException("null NetworkCapabilities");
try {
addCallbackListener();
if (action == LISTEN) {
networkRequest = mService.listenForNetwork(need, new Messenger(sCallbackHandler),
new Binder());
} else {
networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler),
timeoutSec, new Binder());
}
if (networkRequest != null) {
synchronized(sNetworkCallbacks) {
sNetworkCallbacks.put(networkRequest, networkCallbacks);
}
}
} catch (RemoteException e) {}
if (networkRequest == null) removeCallbackListener();
return networkRequest;
}
/**
* Request a network to satisfy a set of {@link NetworkCapabilities}.
*
* This {@link NetworkRequest} will live until released via
* {@link releaseNetworkRequest} or the calling application exits.
* Status of the request can be follwed by listening to the various
* callbacks described in {@link NetworkCallbacks}. The {@link Network}
* can be used by using the {@link bindSocketToNetwork},
* {@link bindApplicationToNetwork} and {@link getAddrInfoOnNetwork} functions.
*
* @param need {@link NetworkCapabilities} required by this request.
* @param networkCallbacks The callbacks to be utilized for this request. Note
* the callbacks can be shared by multiple requests and
* the NetworkRequest token utilized to determine to which
* request the callback relates.
* @return A {@link NetworkRequest} object identifying the request.
* @hide
*/
public NetworkRequest requestNetwork(NetworkCapabilities need,
NetworkCallbacks networkCallbacks) {
return somethingForNetwork(need, networkCallbacks, 0, REQUEST);
}
/**
* Request a network to satisfy a set of {@link NetworkCapabilities}, limited
* by a timeout.
*
* This function behaves identically, but if a suitable network is not found
* within the given time (in Seconds) the {@link NetworkCallbacks#unavailable}
* callback is called. The request must still be released normally by
* calling {@link releaseNetworkRequest}.
* @param need {@link NetworkCapabilities} required by this request.
* @param networkCallbacks The callbacks to be utilized for this request. Note
* the callbacks can be shared by multiple requests and
* the NetworkRequest token utilized to determine to which
* request the callback relates.
* @param timeoutSec The time in seconds to attempt looking for a suitable network
* before {@link NetworkCallbacks#unavailable} is called.
* @return A {@link NetworkRequest} object identifying the request.
* @hide
*/
public NetworkRequest requestNetwork(NetworkCapabilities need,
NetworkCallbacks networkCallbacks, int timeoutSec) {
return somethingForNetwork(need, networkCallbacks, timeoutSec, REQUEST);
}
/**
* The maximum number of seconds the framework will look for a suitable network
* during a timeout-equiped call to {@link requestNetwork}.
* {@hide}
*/
public final static int MAX_NETWORK_REQUEST_TIMEOUT_SEC = 100 * 60;
/**
* Request a network to satisfy a set of {@link NetworkCapabilities}.
*
* This function behavies identically, but instead of {@link NetworkCallbacks}
* a {@link PendingIntent} is used. This means the request may outlive the
* calling application and get called back when a suitable network is found.
* <p>
* The operation is an Intent broadcast that goes to a broadcast receiver that
* you registered with {@link Context#registerReceiver} or through the
* &lt;receiver&gt; tag in an AndroidManifest.xml file
* <p>
* The operation Intent is delivered with two extras, a {@link Network} typed
* extra called {@link EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkCapabilities}
* typed extra called {@link EXTRA_NETWORK_REQUEST_NETWORK_CAPABILTIES} containing
* the original requests parameters. It is important to create a new,
* {@link NetworkCallbacks} based request before completing the processing of the
* Intent to reserve the network or it will be released shortly after the Intent
* is processed.
* <p>
* If there is already an request for this Intent registered (with the equality of
* two Intents defined by {@link Intent#filterEquals}), then it will be removed and
* replace by this one, effectively releasing the previous {@link NetworkRequest}.
* <p>
* The request may be released normally by calling {@link releaseNetworkRequest}.
*
* @param need {@link NetworkCapabilties} required by this request.
* @param operation Action to perform when the network is available (corresponds
* to the {@link NetworkCallbacks#onAvailable} call. Typically
* comes from {@link PendingIntent#getBroadcast}.
* @return A {@link NetworkRequest} object identifying the request.
* @hide
*/
public NetworkRequest requestNetwork(NetworkCapabilities need, PendingIntent operation) {
try {
return mService.pendingRequestForNetwork(need, operation);
} catch (RemoteException e) {}
return null;
}
/**
* Registers to receive notifications about all networks which satisfy the given
* {@link NetworkCapabilities}. The callbacks will continue to be called until
* either the application exits or the request is released using
* {@link releaseNetworkRequest}.
*
* @param need {@link NetworkCapabilities} required by this request.
* @param networkCallbacks The {@link NetworkCallbacks} to be called as suitable
* networks change state.
* @return A {@link NetworkRequest} object identifying the request.
* @hide
*/
public NetworkRequest listenForNetwork(NetworkCapabilities need,
NetworkCallbacks networkCallbacks) {
return somethingForNetwork(need, networkCallbacks, 0, LISTEN);
}
/**
* Releases a {NetworkRequest} generated either through a {@link requestNetwork}
* or a {@link listenForNetwork} call. The {@link NetworkCallbacks} given in the
* earlier call may continue receiving calls until the {@link NetworkCallbacks#onReleased}
* function is called, signifiying the end of the request.
*
* @param networkRequest The {@link NetworkRequest} generated by an earlier call to
* {@link requestNetwork} or {@link listenForNetwork}.
* @hide
*/
public void releaseNetworkRequest(NetworkRequest networkRequest) {
if (networkRequest == null) throw new IllegalArgumentException("null NetworkRequest");
try {
mService.releaseNetworkRequest(networkRequest);
} catch (RemoteException e) {}
}
}

View File

@@ -16,11 +16,14 @@
package android.net;
import android.app.PendingIntent;
import android.net.LinkQualityInfo;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.ProxyInfo;
import android.os.IBinder;
@@ -52,7 +55,10 @@ interface IConnectivityManager
boolean isNetworkSupported(int networkType);
LinkProperties getActiveLinkProperties();
LinkProperties getLinkProperties(int networkType);
LinkProperties getLinkPropertiesForType(int networkType);
LinkProperties getLinkProperties(in Network network);
NetworkCapabilities getNetworkCapabilities(in Network network);
NetworkState[] getAllNetworkState();
@@ -100,6 +106,8 @@ interface IConnectivityManager
void reportInetCondition(int networkType, int percentage);
void reportBadNetwork(in Network network);
ProxyInfo getGlobalProxy();
void setGlobalProxy(in ProxyInfo p);
@@ -147,5 +155,20 @@ interface IConnectivityManager
void registerNetworkFactory(in Messenger messenger);
void registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp, in NetworkCapabilities nc, int score);
void registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
in NetworkCapabilities nc, int score);
NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
in Messenger messenger, int timeoutSec, in IBinder binder);
NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
in PendingIntent operation);
NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
in Messenger messenger, in IBinder binder);
void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
in PendingIntent operation);
void releaseNetworkRequest(in NetworkRequest networkRequest);
}

View File

@@ -384,7 +384,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 14;
/**
* user internally to indicate that data sampling interval is up
* used internally to indicate that data sampling interval is up
*/
private static final int EVENT_SAMPLE_INTERVAL_ELAPSED = 15;
@@ -405,6 +405,32 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private static final int EVENT_REGISTER_NETWORK_AGENT = 18;
/**
* used to add a network request
* includes a NetworkRequestInfo
*/
private static final int EVENT_REGISTER_NETWORK_REQUEST = 19;
/**
* indicates a timeout period is over - check if we had a network yet or not
* and if not, call the timeout calback (but leave the request live until they
* cancel it.
* includes a NetworkRequestInfo
*/
private static final int EVENT_TIMEOUT_NETWORK_REQUEST = 20;
/**
* used to add a network listener - no request
* includes a NetworkRequestInfo
*/
private static final int EVENT_REGISTER_NETWORK_LISTENER = 21;
/**
* used to remove a network request, either a listener or a real request
* includes a NetworkRequest
*/
private static final int EVENT_RELEASE_NETWORK_REQUEST = 22;
/** Handler used for internal events. */
final private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
@@ -498,7 +524,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
mDefaultRequest = new NetworkRequest(netCap, true);
mNetworkRequests.append(mDefaultRequest.requestId, mDefaultRequest);
NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(),
NetworkRequestInfo.REQUEST);
mNetworkRequests.put(mDefaultRequest, nri);
HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
handlerThread.start();
@@ -1058,19 +1086,35 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
@Override
public LinkProperties getActiveLinkProperties() {
return getLinkProperties(mActiveDefaultNetwork);
return getLinkPropertiesForType(mActiveDefaultNetwork);
}
@Override
public LinkProperties getLinkProperties(int networkType) {
public LinkProperties getLinkPropertiesForType(int networkType) {
enforceAccessPermission();
if (isNetworkTypeValid(networkType)) {
return getLinkPropertiesForType(networkType);
return getLinkPropertiesForTypeInternal(networkType);
}
return null;
}
// TODO - this should be ALL networks
@Override
public LinkProperties getLinkProperties(Network network) {
enforceAccessPermission();
NetworkAgentInfo nai = mNetworkForNetId.get(network.netId);
if (nai != null) return new LinkProperties(nai.linkProperties);
return null;
}
@Override
public NetworkCapabilities getNetworkCapabilities(Network network) {
enforceAccessPermission();
NetworkAgentInfo nai = mNetworkForNetId.get(network.netId);
if (nai != null) return new NetworkCapabilities(nai.networkCapabilities);
return null;
}
@Override
public NetworkState[] getAllNetworkState() {
enforceAccessPermission();
@@ -1081,7 +1125,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
networkType++) {
if (getNetworkInfoForType(networkType) != null) {
final NetworkInfo info = getFilteredNetworkInfo(networkType, uid);
final LinkProperties lp = getLinkPropertiesForType(networkType);
final LinkProperties lp = getLinkPropertiesForTypeInternal(networkType);
final NetworkCapabilities netcap = getNetworkCapabilitiesForType(networkType);
result.add(new NetworkState(info, lp, netcap));
}
@@ -1095,7 +1139,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
NetworkInfo info = getNetworkInfoForType(networkType);
if (info != null) {
return new NetworkState(info,
getLinkPropertiesForType(networkType),
getLinkPropertiesForTypeInternal(networkType),
getNetworkCapabilitiesForType(networkType));
}
}
@@ -3002,7 +3046,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* to the link that may have incorrectly setup by the lower
* levels.
*/
LinkProperties lp = getLinkProperties(info.getType());
LinkProperties lp = getLinkPropertiesForTypeInternal(info.getType());
if (DBG) {
log("EVENT_STATE_CHANGED: connected to provisioning network, lp=" + lp);
}
@@ -3050,11 +3094,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
if (VDBG) log("NetworkFactory connected");
// A network factory has connected. Send it all current NetworkRequests.
for (int i = 0; i < mNetworkRequests.size(); i++) {
NetworkRequest request = mNetworkRequests.valueAt(i);
NetworkAgentInfo nai = mNetworkForRequestId.get(request.requestId);
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK,
(nai != null ? nai.currentScore : 0), 0, request);
(nai != null ? nai.currentScore : 0), 0, nri.request);
}
} else {
loge("Error connecting NetworkFactory");
@@ -3072,13 +3115,18 @@ public class ConnectivityService extends IConnectivityManager.Stub {
try {
mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai);
} catch (NullPointerException e) {}
if (nai != null) {
mNetworkForNetId.remove(nai.network.netId);
}
}
}
}
private void handleAsyncChannelDisconnected(Message msg) {
NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
if (nai != null) {
if (DBG) log(nai.name() + " got DISCONNECTED");
if (DBG) {
log(nai.name() + " got DISCONNECTED, was satisfying " + nai.networkRequests.size());
}
// A network agent has disconnected.
// Tell netd to clean up the configuration for this network
// (routing rules, DNS, etc).
@@ -3087,7 +3135,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} catch (Exception e) {
loge("Exception removing network: " + e);
}
notifyNetworkCallbacks(nai, NetworkCallbacks.LOST);
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
updateClat(null, nai.linkProperties, nai);
@@ -3095,12 +3143,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mNetworkAgentInfoForType[nai.networkInfo.getType()].remove(nai);
} catch (NullPointerException e) {}
mNetworkForNetId.remove(nai.network.netId);
// Since we've lost the network, go through all the requests that
// it was satisfying and see if any other factory can satisfy them.
final ArrayList<NetworkAgentInfo> toActivate = new ArrayList<NetworkAgentInfo>();
for (int i = 0; i < nai.networkRequests.size(); i++) {
NetworkRequest request = nai.networkRequests.valueAt(i);
NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId);
if (VDBG) {
log(" checking request " + request + ", currentNetwork = " +
currentNetwork != null ? currentNetwork.name() : "null");
}
if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
mNetworkForRequestId.remove(request.requestId);
sendUpdatedScoreToFactories(request, 0);
@@ -3130,6 +3183,77 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
private void handleRegisterNetworkRequest(Message msg) {
final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
final NetworkCapabilities newCap = nri.request.networkCapabilities;
int score = 0;
// Check for the best currently alive network that satisfies this request
NetworkAgentInfo bestNetwork = null;
for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
if (VDBG) log("handleRegisterNetworkRequest checking " + network.name());
if (newCap.satisfiedByNetworkCapabilities(network.networkCapabilities)) {
if (VDBG) log("apparently satisfied. currentScore=" + network.currentScore);
if ((bestNetwork == null) || bestNetwork.currentScore < network.currentScore) {
bestNetwork = network;
}
}
}
if (bestNetwork != null) {
if (VDBG) log("using " + bestNetwork.name());
bestNetwork.networkRequests.put(nri.request.requestId, nri.request);
notifyNetworkCallback(bestNetwork, nri);
score = bestNetwork.currentScore;
}
mNetworkRequests.put(nri.request, nri);
if (msg.what == EVENT_REGISTER_NETWORK_REQUEST) {
if (DBG) log("sending new NetworkRequest to factories");
for (AsyncChannel ac : mNetworkFactories) {
ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request);
}
}
}
private void handleReleaseNetworkRequest(NetworkRequest request) {
if (DBG) log("releasing NetworkRequest " + request);
NetworkRequestInfo nri = mNetworkRequests.remove(request);
if (nri != null) {
// tell the network currently servicing this that it's no longer interested
NetworkAgentInfo affectedNetwork = mNetworkForRequestId.get(nri.request.requestId);
if (affectedNetwork != null) {
affectedNetwork.networkRequests.remove(nri.request.requestId);
if (VDBG) {
log(" Removing from current network " + affectedNetwork.name() + ", leaving " +
affectedNetwork.networkRequests.size() + " requests.");
}
}
if (nri.isRequest) {
for (AsyncChannel factory : mNetworkFactories) {
factory.sendMessage(NetworkFactoryProtocol.CMD_CANCEL_REQUEST, nri.request);
}
if (affectedNetwork != null) {
// check if this network still has live requests - otherwise, tear down
// TODO - probably push this to the NF/NA
boolean keep = false;
for (int i = 0; i < affectedNetwork.networkRequests.size(); i++) {
NetworkRequest r = affectedNetwork.networkRequests.valueAt(i);
if (mNetworkRequests.get(r).isRequest) {
keep = true;
break;
}
}
if (keep == false) {
if (DBG) log("no live requests for " + affectedNetwork.name() +
"; disconnecting");
affectedNetwork.asyncChannel.disconnect();
}
}
}
callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED);
}
}
private class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
@@ -3232,6 +3356,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj);
break;
}
case EVENT_REGISTER_NETWORK_REQUEST:
case EVENT_REGISTER_NETWORK_LISTENER: {
handleRegisterNetworkRequest(msg);
break;
}
case EVENT_RELEASE_NETWORK_REQUEST: {
handleReleaseNetworkRequest((NetworkRequest) msg.obj);
break;
}
}
}
}
@@ -3378,6 +3511,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
EVENT_INET_CONDITION_CHANGE, networkType, percentage));
}
public void reportBadNetwork(Network network) {
//TODO
}
private void handleInetConditionChange(int netType, int condition) {
if (mActiveDefaultNetwork == -1) {
if (DBG) log("handleInetConditionChange: no active default network - ignore");
@@ -4448,7 +4585,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
log("isMobileOk: addresses=" + inetAddressesToString(addresses));
// Get the type of addresses supported by this link
LinkProperties lp = mCs.getLinkProperties(
LinkProperties lp = mCs.getLinkPropertiesForTypeInternal(
ConnectivityManager.TYPE_MOBILE_HIPRI);
boolean linkHasIpv4 = lp.hasIPv4Address();
boolean linkHasIpv6 = lp.hasIPv6Address();
@@ -5079,7 +5216,109 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
private final ArrayList<AsyncChannel> mNetworkFactories = new ArrayList<AsyncChannel>();
private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
new HashMap<NetworkRequest, NetworkRequestInfo>();
private class NetworkRequestInfo implements IBinder.DeathRecipient {
static final boolean REQUEST = true;
static final boolean LISTEN = false;
final NetworkRequest request;
IBinder mBinder;
final int mPid;
final int mUid;
final Messenger messenger;
final boolean isRequest;
NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, boolean isRequest) {
super();
messenger = m;
request = r;
mBinder = binder;
mPid = getCallingPid();
mUid = getCallingUid();
this.isRequest = isRequest;
try {
mBinder.linkToDeath(this, 0);
} catch (RemoteException e) {
binderDied();
}
}
void unlinkDeathRecipient() {
mBinder.unlinkToDeath(this, 0);
}
public void binderDied() {
log("ConnectivityService NetworkRequestInfo binderDied(" +
request + ", " + mBinder + ")");
releaseNetworkRequest(request);
}
}
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, int timeoutSec, IBinder binder) {
if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
== false) {
enforceConnectivityInternalPermission();
} else {
enforceChangePermission();
}
if (timeoutSec < 0 || timeoutSec > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_SEC) {
throw new IllegalArgumentException("Bad timeout specified");
}
NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
networkCapabilities));
if (DBG) log("requestNetwork for " + networkRequest);
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
NetworkRequestInfo.REQUEST);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
if (timeoutSec > 0) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST,
nri), timeoutSec * 1000);
}
return networkRequest;
}
@Override
public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
PendingIntent operation) {
// TODO
return null;
}
@Override
public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, IBinder binder) {
enforceAccessPermission();
NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
networkCapabilities));
if (DBG) log("listenForNetwork for " + networkRequest);
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
NetworkRequestInfo.LISTEN);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
return networkRequest;
}
@Override
public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
PendingIntent operation) {
}
@Override
public void releaseNetworkRequest(NetworkRequest networkRequest) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST,
networkRequest));
}
@Override
public void registerNetworkFactory(Messenger messenger) {
enforceConnectivityInternalPermission();
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, messenger));
@@ -5090,11 +5329,16 @@ public class ConnectivityService extends IConnectivityManager.Stub {
AsyncChannel ac = new AsyncChannel();
mNetworkFactories.add(ac);
ac.connect(mContext, mTrackerHandler, messenger);
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (nri.isRequest) {
int score = 0;
NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
if (currentNetwork != null) score = currentNetwork.currentScore;
ac.sendMessage(NetworkFactoryProtocol.CMD_REQUEST_NETWORK, score, 0, nri.request);
}
}
}
// NetworkRequest by requestId
private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<NetworkRequest>();
/**
* NetworkAgentInfo supporting a request by requestId.
* These have already been vetted (their Capabilities satisfy the request)
@@ -5104,6 +5348,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
new SparseArray<NetworkAgentInfo>();
private final SparseArray<NetworkAgentInfo> mNetworkForNetId =
new SparseArray<NetworkAgentInfo>();
// NetworkAgentInfo keyed off its connecting messenger
// TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays
private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
@@ -5131,6 +5378,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} catch (NullPointerException e) {
loge("registered NetworkAgent for unsupported type: " + na);
}
mNetworkForNetId.put(na.network.netId, na);
na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
NetworkInfo networkInfo = na.networkInfo;
na.networkInfo = null;
@@ -5266,9 +5514,47 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
private void callCallbackForRequest(NetworkRequest networkRequest,
private void callCallbackForRequest(NetworkRequestInfo nri,
NetworkAgentInfo networkAgent, int notificationType) {
// TODO
if (nri.messenger == null) return; // Default request has no msgr
Object o;
int a1 = 0;
int a2 = 0;
switch (notificationType) {
case ConnectivityManager.CALLBACK_LOSING:
a1 = 30; // TODO - read this from NetworkMonitor
// fall through
case ConnectivityManager.CALLBACK_PRECHECK:
case ConnectivityManager.CALLBACK_AVAILABLE:
case ConnectivityManager.CALLBACK_LOST:
case ConnectivityManager.CALLBACK_CAP_CHANGED:
case ConnectivityManager.CALLBACK_IP_CHANGED: {
o = new NetworkRequest(nri.request);
a2 = networkAgent.network.netId;
break;
}
case ConnectivityManager.CALLBACK_UNAVAIL:
case ConnectivityManager.CALLBACK_RELEASED: {
o = new NetworkRequest(nri.request);
break;
}
default: {
loge("Unknown notificationType " + notificationType);
return;
}
}
Message msg = Message.obtain();
msg.arg1 = a1;
msg.arg2 = a2;
msg.obj = o;
msg.what = notificationType;
try {
if (VDBG) log("sending notification " + notificationType + " for " + nri.request);
nri.messenger.send(msg);
} catch (RemoteException e) {
// may occur naturally in the race of binder death.
loge("RemoteException caught trying to send a callback msg for " + nri.request);
}
}
private void handleLingerComplete(NetworkAgentInfo oldNetwork) {
@@ -5295,13 +5581,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (DBG) log("handleConnectionValidated for "+newNetwork.name());
// check if any NetworkRequest wants this NetworkAgent
// first check if it satisfies the NetworkCapabilities
for (int i = 0; i < mNetworkRequests.size(); i++) {
NetworkRequest nr = mNetworkRequests.valueAt(i);
if (nr.networkCapabilities.satisfiedByNetworkCapabilities(
ArrayList<NetworkAgentInfo> affectedNetworks = new ArrayList<NetworkAgentInfo>();
if (VDBG) log(" new Network has: " + newNetwork.networkCapabilities);
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (VDBG) log(" checking if request is satisfied: " + nri.request);
if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities(
newNetwork.networkCapabilities)) {
// next check if it's better than any current network we're using for
// this request
NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nr.requestId);
NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
if (VDBG) {
log("currentScore = " +
(currentNetwork != null ? currentNetwork.currentScore : 0) +
@@ -5310,28 +5598,52 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (currentNetwork == null ||
currentNetwork.currentScore < newNetwork.currentScore) {
if (currentNetwork != null) {
currentNetwork.networkRequests.remove(nr.requestId);
currentNetwork.networkListens.add(nr);
if (currentNetwork.networkRequests.size() == 0) {
currentNetwork.networkMonitor.sendMessage(
NetworkMonitor.CMD_NETWORK_LINGER);
notifyNetworkCallbacks(currentNetwork, NetworkCallbacks.LOSING);
}
if (VDBG) log(" accepting network in place of " + currentNetwork.name());
currentNetwork.networkRequests.remove(nri.request.requestId);
currentNetwork.networkLingered.add(nri.request);
affectedNetworks.add(currentNetwork);
} else {
if (VDBG) log(" accepting network in place of null");
}
mNetworkForRequestId.put(nr.requestId, newNetwork);
newNetwork.networkRequests.put(nr.requestId, nr);
mNetworkForRequestId.put(nri.request.requestId, newNetwork);
newNetwork.networkRequests.put(nri.request.requestId, nri.request);
keep = true;
// TODO - this could get expensive if we have alot of requests for this
// network. Think about if there is a way to reduce this. Push
// netid->request mapping to each factory?
sendUpdatedScoreToFactories(nr, newNetwork.currentScore);
if (mDefaultRequest.requestId == nr.requestId) {
sendUpdatedScoreToFactories(nri.request, newNetwork.currentScore);
if (mDefaultRequest.requestId == nri.request.requestId) {
isNewDefault = true;
updateActiveDefaultNetwork(newNetwork);
}
}
}
}
for (NetworkAgentInfo nai : affectedNetworks) {
boolean teardown = true;
for (int i = 0; i < nai.networkRequests.size(); i++) {
NetworkRequest nr = nai.networkRequests.valueAt(i);
try {
if (mNetworkRequests.get(nr).isRequest) {
teardown = false;
}
} catch (Exception e) {
loge("Request " + nr + " not found in mNetworkRequests.");
loge(" it came from request list of " + nai.name());
}
}
if (teardown) {
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_LINGER);
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING);
} else {
// not going to linger, so kill the list of linger networks.. only
// notify them of linger if it happens as the result of gaining another,
// but if they transition and old network stays up, don't tell them of linger
// or very delayed loss
nai.networkLingered.clear();
if (VDBG) log("Lingered for " + nai.name() + " cleared");
}
}
if (keep) {
if (isNewDefault) {
if (VDBG) log("Switching to new default network: " + newNetwork);
@@ -5370,8 +5682,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// TODO
//BatteryStatsService.getService().noteNetworkInterfaceType(iface, netType);
// } catch (RemoteException e) { }
notifyNetworkCallbacks(newNetwork, NetworkCallbacks.AVAILABLE);
} else if (newNetwork.networkRequests.size() == 0) {
notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_AVAILABLE);
} else {
if (DBG && newNetwork.networkRequests.size() != 0) {
loge("tearing down network with live requests:");
for (int i=0; i < newNetwork.networkRequests.size(); i++) {
loge(" " + newNetwork.networkRequests.valueAt(i));
}
}
if (VDBG) log("Validated network turns out to be unwanted. Tear it down.");
newNetwork.asyncChannel.disconnect();
}
@@ -5401,7 +5719,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loge("Error creating Network " + networkAgent.network.netId);
}
updateLinkProperties(networkAgent, null);
notifyNetworkCallbacks(networkAgent, NetworkCallbacks.PRECHECK);
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
} else if (state == NetworkInfo.State.DISCONNECTED ||
state == NetworkInfo.State.SUSPENDED) {
@@ -5409,24 +5727,37 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
// notify only this one new request of the current state
protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
// TODO - read state from monitor to decide what to send.
// if (nai.networkMonitor.isLingering()) {
// notifyType = NetworkCallbacks.LOSING;
// } else if (nai.networkMonitor.isEvaluating()) {
// notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType);
// }
if (nri.request.needsBroadcasts) {
// TODO
// sendNetworkBroadcast(nai, notifyType);
}
callCallbackForRequest(nri, nai, notifyType);
}
protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType) {
if (VDBG) log("notifyType " + notifyType + " for " + networkAgent.name());
boolean needsBroadcasts = false;
for (int i = 0; i < networkAgent.networkRequests.size(); i++) {
NetworkRequest request = networkAgent.networkRequests.valueAt(i);
if (request == null) continue;
if (request.needsBroadcasts) needsBroadcasts = true;
callCallbackForRequest(request, networkAgent, notifyType);
}
for (NetworkRequest request : networkAgent.networkListens) {
if (request.needsBroadcasts) needsBroadcasts = true;
callCallbackForRequest(request, networkAgent, notifyType);
NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
if (VDBG) log(" sending notification for " + nr);
if (nr.needsBroadcasts) needsBroadcasts = true;
callCallbackForRequest(nri, networkAgent, notifyType);
}
if (needsBroadcasts) {
if (notifyType == NetworkCallbacks.AVAILABLE) {
if (notifyType == ConnectivityManager.CALLBACK_AVAILABLE) {
sendConnectedBroadcastDelayed(networkAgent.networkInfo,
getConnectivityChangeDelay());
} else if (notifyType == NetworkCallbacks.LOST) {
} else if (notifyType == ConnectivityManager.CALLBACK_LOST) {
NetworkInfo info = new NetworkInfo(networkAgent.networkInfo);
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
@@ -5465,7 +5796,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
private LinkProperties getLinkPropertiesForType(int networkType) {
private LinkProperties getLinkPropertiesForTypeInternal(int networkType) {
ArrayList<NetworkAgentInfo> list = mNetworkAgentInfoForType[networkType];
if (list == null) return null;
try {

View File

@@ -48,9 +48,8 @@ public class NetworkAgentInfo {
// The list of NetworkRequests being satisfied by this Network.
public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
// The list of NetworkListens listening for changes on this Network.
public final ArrayList<NetworkRequest> networkListens = new ArrayList<NetworkRequest>();
public final Messenger messenger;
public final AsyncChannel asyncChannel;