diff options
6 files changed, 811 insertions, 62 deletions
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 1ee4ec0..e14f555 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -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; } @@ -1303,6 +1330,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 * doing something unusual like general internal filtering this may be useful. On @@ -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 + * <receiver> 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) {} + } } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 0d2e14d..885b8b6 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -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); } diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java index c7591f4..4b84941 100644 --- a/core/java/com/android/internal/util/Protocol.java +++ b/core/java/com/android/internal/util/Protocol.java @@ -58,5 +58,6 @@ public class Protocol { public static final int BASE_CONNECTIVITY_SERVICE = 0x00080000; public static final int BASE_NETWORK_AGENT = 0x00081000; public static final int BASE_NETWORK_MONITOR = 0x00082000; + public static final int BASE_CONNECTIVITY_MANAGER = 0x00083000; //TODO: define all used protocols } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 1857aa9..0708e55 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -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,20 +1086,36 @@ 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(); final int uid = Binder.getCallingUid(); @@ -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)); } } @@ -2997,7 +3041,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); } @@ -3045,11 +3089,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"); @@ -3067,13 +3110,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). @@ -3082,7 +3130,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); @@ -3090,12 +3138,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); @@ -3125,6 +3178,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) { @@ -3227,6 +3351,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; + } } } } @@ -3373,6 +3506,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"); @@ -4443,7 +4580,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(); @@ -5074,7 +5211,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)); @@ -5085,11 +5324,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) @@ -5099,6 +5343,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 = @@ -5126,6 +5373,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; @@ -5261,9 +5509,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) { @@ -5290,13 +5576,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) + @@ -5305,28 +5593,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); @@ -5365,8 +5677,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(); } @@ -5396,7 +5714,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) { @@ -5404,24 +5722,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); @@ -5460,7 +5791,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 { diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 0c568b7..8102591 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -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; diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index abe362a..92b5f52 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -1325,7 +1325,7 @@ public class Tethering extends BaseNetworkObserver { } else { LinkProperties linkProperties = null; try { - linkProperties = mConnService.getLinkProperties(upType); + linkProperties = mConnService.getLinkPropertiesForType(upType); } catch (RemoteException e) { } if (linkProperties != null) { // Find the interface with the default IPv4 route. It may be the |
