diff options
author | Chalard Jean <jchalard@google.com> | 2021-04-19 18:04:34 +0900 |
---|---|---|
committer | David Su <dysu@google.com> | 2021-05-03 23:09:41 -0700 |
commit | 0ff1a526c4b29bb15a90c79ccd971ab190f73045 (patch) | |
tree | 3a113fef8e19a2b0d880fd759a773c302fd557aa | |
parent | 171fe2223dcc02228ecc7261dd1226a8d307e2a5 (diff) | |
download | net-0ff1a526c4b29bb15a90c79ccd971ab190f73045.tar.gz |
Support both R and S+ in NetworkFactory
NetworkFactory needs to be backwards compatible as
it is statically linked by the Wifi module, which
needs to run on both R and S.
To achieve this, maintain 2 separate implementations
called NetworkFactoryImpl (S+) and
NetworkFactoryLegacyImpl (R). NetworkFactory itself
becomes a shim that delegates to one of these
implementations depending on the device's SDK version.
Ignore-AOSP-First: Merge in internal first to fix
S-on-R test breakages.
Bug: 183902758
Test: ClientModeImplTest
OemWifiNetworkFactoryTest
TelephonyNetworkFactoryTest
UntrustedWifiNetworkFactoryTest
WifiNetworkFactoryTest
Change-Id: I6a08f39ad1633dd7c1582c362c97821e24ce6683
-rw-r--r-- | common/Android.bp | 19 | ||||
-rw-r--r-- | common/device/android/net/NetworkFactory.java | 302 | ||||
-rw-r--r-- | common/device/android/net/NetworkFactoryImpl.java | 322 | ||||
-rw-r--r-- | common/device/android/net/NetworkFactoryLegacyImpl.java | 398 | ||||
-rw-r--r-- | common/device/android/net/NetworkFactoryShim.java | 71 | ||||
-rw-r--r-- | common/jarjar-rules-shared.txt | 1 |
6 files changed, 854 insertions, 259 deletions
diff --git a/common/Android.bp b/common/Android.bp index f52a4afe..3dbc09f1 100644 --- a/common/Android.bp +++ b/common/Android.bp @@ -34,9 +34,9 @@ package { java_library { name: "net-utils-device-common", srcs: [ - "device/**/*.java", + "device/com/android/net/module/util/**/*.java", // This library is used by system modules, for which the system health impact of Kotlin - // has not yet been evaluated. + // has not yet been evaluated. Annotations may need jarjar'ing. // "src_devicecommon/**/*.kt", ":framework-annotations", ], @@ -117,13 +117,15 @@ java_library { "//frameworks/libs/net/common/tests:__subpackages__", "//frameworks/libs/net/common/device", "//packages/modules/Wifi/framework/tests:__subpackages__", - ] + ], } - filegroup { name: "net-utils-services-common-srcs", srcs: [ "device/android/net/NetworkFactory.java", + "device/android/net/NetworkFactoryImpl.java", + "device/android/net/NetworkFactoryLegacyImpl.java", + "device/android/net/NetworkFactoryShim.java", ], visibility: [ "//frameworks/base/services/net", @@ -138,6 +140,9 @@ java_library { ], sdk_version: "system_current", min_sdk_version: "30", + static_libs: [ + "modules-utils-build_system", + ], visibility: [ "//frameworks/base/services/net", ], @@ -150,6 +155,9 @@ filegroup { srcs: [ // Any class here *must* have a corresponding jarjar rule in the telephony build rules. "device/android/net/NetworkFactory.java", + "device/android/net/NetworkFactoryImpl.java", + "device/android/net/NetworkFactoryLegacyImpl.java", + "device/android/net/NetworkFactoryShim.java", ], path: "device", visibility: [ @@ -178,6 +186,9 @@ filegroup { name: "net-utils-wifi-service-common-srcs", srcs: [ "device/android/net/NetworkFactory.java", + "device/android/net/NetworkFactoryImpl.java", + "device/android/net/NetworkFactoryLegacyImpl.java", + "device/android/net/NetworkFactoryShim.java", "framework/com/android/net/module/util/NetUtils.java", ], visibility: [ diff --git a/common/device/android/net/NetworkFactory.java b/common/device/android/net/NetworkFactory.java index 68db57b5..87f6dee6 100644 --- a/common/device/android/net/NetworkFactory.java +++ b/common/device/android/net/NetworkFactory.java @@ -16,11 +16,11 @@ package android.net; +import static com.android.modules.utils.build.SdkLevel.isAtLeastS; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.net.NetworkProvider.NetworkOfferCallback; -import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; @@ -29,9 +29,6 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.Executor; /** * A NetworkFactory is an entity that creates NetworkAgent objects. @@ -46,108 +43,54 @@ import java.util.concurrent.Executor; * functions can be overridden. If the bearer needs more interaction, it can * override addNetworkRequest and removeNetworkRequest which will give it each * request that passes their current filters. + * + * This class is mostly a shim which delegates to one of two implementations depending + * on the SDK level of the device it's running on. + * * @hide **/ -public class NetworkFactory extends Handler { - private static final boolean DBG = true; - private static final boolean VDBG = false; +public class NetworkFactory { + static final boolean DBG = true; + static final boolean VDBG = false; - // A score that will win against everything, so that score filtering will let all requests - // through - // TODO : remove this and replace with an API to listen to all requests. - @NonNull - private static final NetworkScore INVINCIBLE_SCORE = - new NetworkScore.Builder().setLegacyInt(1000).build(); - - /** - * Pass a network request to the bearer. If the bearer believes it can - * satisfy the request it should connect to the network and create a - * NetworkAgent. Once the NetworkAgent is fully functional it will - * register itself with ConnectivityService using registerNetworkAgent. - * If the bearer cannot immediately satisfy the request (no network, - * user disabled the radio, lower-scored network) it should remember - * any NetworkRequests it may be able to satisfy in the future. It may - * disregard any that it will never be able to service, for example - * those requiring a different bearer. - * msg.obj = NetworkRequest - */ - // TODO : this and CANCEL_REQUEST are only used by telephony tests. Replace it in the tests - // and remove them and the associated code. - public static final int CMD_REQUEST_NETWORK = 1; - - /** - * Cancel a network request - * msg.obj = NetworkRequest - */ - public static final int CMD_CANCEL_REQUEST = 2; + final NetworkFactoryShim mImpl; - /** - * Internally used to set our best-guess score. - * msg.obj = new score - */ - private static final int CMD_SET_SCORE = 3; - - /** - * Internally used to set our current filter for coarse bandwidth changes with - * technology changes. - * msg.obj = new filter - */ - private static final int CMD_SET_FILTER = 4; - - /** - * Internally used to send the network offer associated with this factory. - * No arguments, will read from members - */ - private static final int CMD_OFFER_NETWORK = 5; - - /** - * Internally used to send the request to listen to all requests. - * No arguments, will read from members - */ - private static final int CMD_LISTEN_TO_ALL_REQUESTS = 6; - - private final Context mContext; private final String LOG_TAG; - private final Map<NetworkRequest, NetworkRequestInfo> mNetworkRequests = - new LinkedHashMap<>(); - - @NonNull private NetworkScore mScore = new NetworkScore.Builder().setLegacyInt(0).build(); - @NonNull private NetworkCapabilities mCapabilityFilter; - - private int mRefCount = 0; - private NetworkProvider mProvider = null; - - private final NetworkOfferCallback mRequestCallback = new NetworkOfferCallback() { - @Override - public void onNetworkNeeded(@NonNull final NetworkRequest request) { - handleAddRequest(request); - } - - @Override - public void onNetworkUnneeded(@NonNull final NetworkRequest request) { - handleRemoveRequest(request); - } - }; - @NonNull private final Executor mExecutor = command -> post(command); - // Ideally the filter argument would be non-null, but null has historically meant to see // no requests and telephony passes null. public NetworkFactory(Looper looper, Context context, String logTag, @Nullable final NetworkCapabilities filter) { - super(looper); LOG_TAG = logTag; - mContext = context; - if (null != filter) { - mCapabilityFilter = filter; + if (isAtLeastS()) { + mImpl = new NetworkFactoryImpl(this, looper, context, filter); } else { - mCapabilityFilter = new NetworkCapabilities.Builder().clearAll().build(); + mImpl = new NetworkFactoryLegacyImpl(this, looper, context, filter); } } + // TODO : these two constants and the method are only used by telephony tests. Replace it in + // the tests and remove them and the associated code. + public static final int CMD_REQUEST_NETWORK = 1; + public static final int CMD_CANCEL_REQUEST = 2; + /** Like Handler#obtainMessage */ + @VisibleForTesting + public Message obtainMessage(final int what, final int arg1, final int arg2, + final @Nullable Object obj) { + return mImpl.obtainMessage(what, arg1, arg2, obj); + } + + // Called by BluetoothNetworkFactory + public final Looper getLooper() { + return mImpl.getLooper(); + } + + // Refcount for simple mode requests + private int mRefCount = 0; + /* Registers this NetworkFactory with the system. May only be called once per factory. */ public void register() { - register(false); + mImpl.register(LOG_TAG); } /** @@ -156,152 +99,16 @@ public class NetworkFactory extends Handler { * May only be called once per factory. */ public void registerIgnoringScore() { - register(true); - } - - private void register(final boolean listenToAllRequests) { - if (mProvider != null) { - throw new IllegalStateException("A NetworkFactory must only be registered once"); - } - if (DBG) log("Registering NetworkFactory"); - - mProvider = new NetworkProvider(mContext, NetworkFactory.this.getLooper(), LOG_TAG) { - @Override - public void onNetworkRequested(@NonNull NetworkRequest request, int score, - int servingProviderId) { - handleAddRequest(request); - } - - @Override - public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) { - handleRemoveRequest(request); - } - }; - - ((ConnectivityManager) mContext.getSystemService( - Context.CONNECTIVITY_SERVICE)).registerNetworkProvider(mProvider); - - // The mScore and mCapabilityFilter members can only be accessed on the handler thread. - // TODO : offer a separate API to listen to all requests instead - if (listenToAllRequests) { - sendMessage(obtainMessage(CMD_LISTEN_TO_ALL_REQUESTS)); - } else { - sendMessage(obtainMessage(CMD_OFFER_NETWORK)); - } - } - - private void handleOfferNetwork(@NonNull final NetworkScore score) { - mProvider.registerNetworkOffer(score, mCapabilityFilter, mExecutor, mRequestCallback); + mImpl.registerIgnoringScore(LOG_TAG); } /** Unregisters this NetworkFactory. After this call, the object can no longer be used. */ public void terminate() { - if (mProvider == null) { - throw new IllegalStateException("This NetworkFactory was never registered"); - } - if (DBG) log("Unregistering NetworkFactory"); - - ((ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE)) - .unregisterNetworkProvider(mProvider); - - // Remove all pending messages, since this object cannot be reused. Any message currently - // being processed will continue to run. - removeCallbacksAndMessages(null); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case CMD_REQUEST_NETWORK: { - handleAddRequest((NetworkRequest) msg.obj); - break; - } - case CMD_CANCEL_REQUEST: { - handleRemoveRequest((NetworkRequest) msg.obj); - break; - } - case CMD_SET_SCORE: { - handleSetScore((NetworkScore) msg.obj); - break; - } - case CMD_SET_FILTER: { - handleSetFilter((NetworkCapabilities) msg.obj); - break; - } - case CMD_OFFER_NETWORK: { - handleOfferNetwork(mScore); - break; - } - case CMD_LISTEN_TO_ALL_REQUESTS: { - handleOfferNetwork(INVINCIBLE_SCORE); - break; - } - } - } - - private static class NetworkRequestInfo { - @NonNull public final NetworkRequest request; - public boolean requested; // do we have a request outstanding, limited by score - - NetworkRequestInfo(@NonNull final NetworkRequest request) { - this.request = request; - this.requested = false; - } - - @Override - public String toString() { - return "{" + request + ", requested=" + requested + "}"; - } - } - - /** - * Add a NetworkRequest that the bearer may want to attempt to satisfy. - * @see #CMD_REQUEST_NETWORK - * - * @param request the request to handle. - */ - private void handleAddRequest(@NonNull final NetworkRequest request) { - NetworkRequestInfo n = mNetworkRequests.get(request); - if (n == null) { - if (DBG) log("got request " + request); - n = new NetworkRequestInfo(request); - mNetworkRequests.put(n.request, n); - } else { - if (VDBG) { - log("handle existing request " + request); - } - } - if (VDBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter); - - if (acceptRequest(request)) { - n.requested = true; - needNetworkFor(request); - } - } - - private void handleRemoveRequest(NetworkRequest request) { - NetworkRequestInfo n = mNetworkRequests.get(request); - if (n != null) { - mNetworkRequests.remove(request); - if (n.requested) releaseNetworkFor(n.request); - } - } - - private void handleSetScore(@NonNull final NetworkScore score) { - if (mScore.equals(score)) return; - mScore = score; - reevaluateAllRequests(); - } - - private void handleSetFilter(@NonNull final NetworkCapabilities netCap) { - if (netCap.equals(mCapabilityFilter)) return; - mCapabilityFilter = netCap; - reevaluateAllRequests(); + mImpl.terminate(); } protected final void reevaluateAllRequests() { - if (mProvider == null) return; - mProvider.registerNetworkOffer(mScore, mCapabilityFilter, mExecutor, mRequestCallback); + mImpl.reevaluateAllRequests(); } /** @@ -326,6 +133,7 @@ public class NetworkFactory extends Handler { public boolean acceptRequest(@NonNull final NetworkRequest request) { return true; } + /** * Can be called by a factory to release a request as unfulfillable: the request will be * removed, and the caller will get a @@ -336,15 +144,7 @@ public class NetworkFactory extends Handler { * is able to fulfill this request! */ protected void releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r) { - post(() -> { - if (DBG) log("releaseRequestAsUnfulfillableByAnyFactory: " + r); - final NetworkProvider provider = mProvider; - if (provider == null) { - Log.e(LOG_TAG, "Ignoring attempt to release unregistered request as unfulfillable"); - return; - } - provider.declareNetworkRequestUnfulfillable(r); - }); + mImpl.releaseRequestAsUnfulfillableByAnyFactory(r); } // override to do simple mode (request independent) @@ -362,11 +162,11 @@ public class NetworkFactory extends Handler { /** * @deprecated this method was never part of the API (system or public) and is only added - * for migration of existing clients. It will be removed before S ships. + * for migration of existing clients. */ @Deprecated public void setScoreFilter(final int score) { - setScoreFilter(new NetworkScore.Builder().setLegacyInt(score).build()); + mImpl.setScoreFilter(score); } /** @@ -380,25 +180,24 @@ public class NetworkFactory extends Handler { * @param score the filter */ public void setScoreFilter(@NonNull final NetworkScore score) { - sendMessage(obtainMessage(CMD_SET_SCORE, score)); + mImpl.setScoreFilter(score); } public void setCapabilityFilter(NetworkCapabilities netCap) { - sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap))); + mImpl.setCapabilityFilter(netCap); } @VisibleForTesting protected int getRequestCount() { - return mNetworkRequests.size(); + return mImpl.getRequestCount(); } - /* TODO: delete when all callers have migrated to NetworkProvider IDs. */ public int getSerialNumber() { - return mProvider.getProviderId(); + return mImpl.getSerialNumber(); } public NetworkProvider getProvider() { - return mProvider; + return mImpl.getProvider(); } protected void log(String s) { @@ -406,18 +205,11 @@ public class NetworkFactory extends Handler { } public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - writer.println(toString()); - for (NetworkRequestInfo n : mNetworkRequests.values()) { - writer.println(" " + n); - } + mImpl.dump(fd, writer, args); } @Override public String toString() { - return "{" + LOG_TAG + " - providerId=" - + mProvider.getProviderId() + ", ScoreFilter=" - + mScore + ", Filter=" + mCapabilityFilter + ", requests=" - + mNetworkRequests.size() + ", refCount=" + mRefCount - + "}"; + return "{" + LOG_TAG + " " + mImpl.toString() + "}"; } } diff --git a/common/device/android/net/NetworkFactoryImpl.java b/common/device/android/net/NetworkFactoryImpl.java new file mode 100644 index 00000000..a3373512 --- /dev/null +++ b/common/device/android/net/NetworkFactoryImpl.java @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.net.NetworkProvider.NetworkOfferCallback; +import android.os.Looper; +import android.os.Message; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.Executor; + +/** + * A NetworkFactory is an entity that creates NetworkAgent objects. + * The bearers register with ConnectivityService using {@link #register} and + * their factory will start receiving scored NetworkRequests. NetworkRequests + * can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by + * overridden function. All of these can be dynamic - changing NetworkCapabilities + * or score forces re-evaluation of all current requests. + * + * If any requests pass the filter some overrideable functions will be called. + * If the bearer only cares about very simple start/stopNetwork callbacks, those + * functions can be overridden. If the bearer needs more interaction, it can + * override addNetworkRequest and removeNetworkRequest which will give it each + * request that passes their current filters. + * @hide + **/ +// TODO(b/187083878): factor out common code between this and NetworkFactoryLegacyImpl +class NetworkFactoryImpl extends NetworkFactoryLegacyImpl { + private static final boolean DBG = NetworkFactory.DBG; + private static final boolean VDBG = NetworkFactory.VDBG; + + // A score that will win against everything, so that score filtering will let all requests + // through + // TODO : remove this and replace with an API to listen to all requests. + @NonNull + private static final NetworkScore INVINCIBLE_SCORE = + new NetworkScore.Builder().setLegacyInt(1000).build(); + + // TODO(b/187082970): Replace CMD_* with Handler.post(() -> { ... }) since all the CMDs do is to + // run the tasks asynchronously on the Handler thread. + + /** + * Pass a network request to the bearer. If the bearer believes it can + * satisfy the request it should connect to the network and create a + * NetworkAgent. Once the NetworkAgent is fully functional it will + * register itself with ConnectivityService using registerNetworkAgent. + * If the bearer cannot immediately satisfy the request (no network, + * user disabled the radio, lower-scored network) it should remember + * any NetworkRequests it may be able to satisfy in the future. It may + * disregard any that it will never be able to service, for example + * those requiring a different bearer. + * msg.obj = NetworkRequest + */ + // TODO : this and CANCEL_REQUEST are only used by telephony tests. Replace it in the tests + // and remove them and the associated code. + private static final int CMD_REQUEST_NETWORK = NetworkFactory.CMD_REQUEST_NETWORK; + + /** + * Cancel a network request + * msg.obj = NetworkRequest + */ + private static final int CMD_CANCEL_REQUEST = NetworkFactory.CMD_CANCEL_REQUEST; + + /** + * Internally used to set our best-guess score. + * msg.obj = new score + */ + private static final int CMD_SET_SCORE = 3; + + /** + * Internally used to set our current filter for coarse bandwidth changes with + * technology changes. + * msg.obj = new filter + */ + private static final int CMD_SET_FILTER = 4; + + /** + * Internally used to send the network offer associated with this factory. + * No arguments, will read from members + */ + private static final int CMD_OFFER_NETWORK = 5; + + /** + * Internally used to send the request to listen to all requests. + * No arguments, will read from members + */ + private static final int CMD_LISTEN_TO_ALL_REQUESTS = 6; + + private final Map<NetworkRequest, NetworkRequestInfo> mNetworkRequests = + new LinkedHashMap<>(); + + @NonNull private NetworkScore mScore = new NetworkScore.Builder().setLegacyInt(0).build(); + + private final NetworkOfferCallback mRequestCallback = new NetworkOfferCallback() { + @Override + public void onNetworkNeeded(@NonNull final NetworkRequest request) { + handleAddRequest(request); + } + + @Override + public void onNetworkUnneeded(@NonNull final NetworkRequest request) { + handleRemoveRequest(request); + } + }; + @NonNull private final Executor mExecutor = command -> post(command); + + + // Ideally the filter argument would be non-null, but null has historically meant to see + // no requests and telephony passes null. + NetworkFactoryImpl(NetworkFactory parent, Looper looper, Context context, + @Nullable final NetworkCapabilities filter) { + super(parent, looper, context, + null != filter ? filter : new NetworkCapabilities.Builder().clearAll().build()); + } + + /* Registers this NetworkFactory with the system. May only be called once per factory. */ + @Override public void register(final String logTag) { + register(logTag, false); + } + + /** + * Registers this NetworkFactory with the system ignoring the score filter. This will let + * the factory always see all network requests matching its capabilities filter. + * May only be called once per factory. + */ + @Override public void registerIgnoringScore(final String logTag) { + register(logTag, true); + } + + private void register(final String logTag, final boolean listenToAllRequests) { + if (mProvider != null) { + throw new IllegalStateException("A NetworkFactory must only be registered once"); + } + if (DBG) mParent.log("Registering NetworkFactory"); + + mProvider = new NetworkProvider(mContext, NetworkFactoryImpl.this.getLooper(), logTag) { + @Override + public void onNetworkRequested(@NonNull NetworkRequest request, int score, + int servingProviderId) { + handleAddRequest(request); + } + + @Override + public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) { + handleRemoveRequest(request); + } + }; + + ((ConnectivityManager) mContext.getSystemService( + Context.CONNECTIVITY_SERVICE)).registerNetworkProvider(mProvider); + + // The mScore and mCapabilityFilter members can only be accessed on the handler thread. + // TODO : offer a separate API to listen to all requests instead + if (listenToAllRequests) { + sendMessage(obtainMessage(CMD_LISTEN_TO_ALL_REQUESTS)); + } else { + sendMessage(obtainMessage(CMD_OFFER_NETWORK)); + } + } + + private void handleOfferNetwork(@NonNull final NetworkScore score) { + mProvider.registerNetworkOffer(score, mCapabilityFilter, mExecutor, mRequestCallback); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case CMD_REQUEST_NETWORK: { + handleAddRequest((NetworkRequest) msg.obj); + break; + } + case CMD_CANCEL_REQUEST: { + handleRemoveRequest((NetworkRequest) msg.obj); + break; + } + case CMD_SET_SCORE: { + handleSetScore((NetworkScore) msg.obj); + break; + } + case CMD_SET_FILTER: { + handleSetFilter((NetworkCapabilities) msg.obj); + break; + } + case CMD_OFFER_NETWORK: { + handleOfferNetwork(mScore); + break; + } + case CMD_LISTEN_TO_ALL_REQUESTS: { + handleOfferNetwork(INVINCIBLE_SCORE); + break; + } + } + } + + private static class NetworkRequestInfo { + @NonNull public final NetworkRequest request; + public boolean requested; // do we have a request outstanding, limited by score + + NetworkRequestInfo(@NonNull final NetworkRequest request) { + this.request = request; + this.requested = false; + } + + @Override + public String toString() { + return "{" + request + ", requested=" + requested + "}"; + } + } + + /** + * Add a NetworkRequest that the bearer may want to attempt to satisfy. + * @see #CMD_REQUEST_NETWORK + * + * @param request the request to handle. + */ + private void handleAddRequest(@NonNull final NetworkRequest request) { + NetworkRequestInfo n = mNetworkRequests.get(request); + if (n == null) { + if (DBG) mParent.log("got request " + request); + n = new NetworkRequestInfo(request); + mNetworkRequests.put(n.request, n); + } else { + if (VDBG) mParent.log("handle existing request " + request); + } + if (VDBG) mParent.log(" my score=" + mScore + ", my filter=" + mCapabilityFilter); + + if (mParent.acceptRequest(request)) { + n.requested = true; + mParent.needNetworkFor(request); + } + } + + private void handleRemoveRequest(NetworkRequest request) { + NetworkRequestInfo n = mNetworkRequests.get(request); + if (n != null) { + mNetworkRequests.remove(request); + if (n.requested) mParent.releaseNetworkFor(n.request); + } + } + + private void handleSetScore(@NonNull final NetworkScore score) { + if (mScore.equals(score)) return; + mScore = score; + mParent.reevaluateAllRequests(); + } + + private void handleSetFilter(@NonNull final NetworkCapabilities netCap) { + if (netCap.equals(mCapabilityFilter)) return; + mCapabilityFilter = netCap; + mParent.reevaluateAllRequests(); + } + + @Override public final void reevaluateAllRequests() { + if (mProvider == null) return; + mProvider.registerNetworkOffer(mScore, mCapabilityFilter, mExecutor, mRequestCallback); + } + + /** + * @deprecated this method was never part of the API (system or public) and is only added + * for migration of existing clients. + */ + @Deprecated + public void setScoreFilter(final int score) { + setScoreFilter(new NetworkScore.Builder().setLegacyInt(score).build()); + } + + /** + * Set a score filter for this factory. + * + * This should include the transports the factory knows its networks will have, and + * an optimistic view of the attributes it may have. This does not commit the factory + * to being able to bring up such a network ; it only lets it avoid hearing about + * requests that it has no chance of fulfilling. + * + * @param score the filter + */ + @Override public void setScoreFilter(@NonNull final NetworkScore score) { + sendMessage(obtainMessage(CMD_SET_SCORE, score)); + } + + @Override public void setCapabilityFilter(NetworkCapabilities netCap) { + sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap))); + } + + @Override public int getRequestCount() { + return mNetworkRequests.size(); + } + + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + writer.println(toString()); + for (NetworkRequestInfo n : mNetworkRequests.values()) { + writer.println(" " + n); + } + } + + @Override public String toString() { + return "providerId=" + + mProvider.getProviderId() + ", ScoreFilter=" + + mScore + ", Filter=" + mCapabilityFilter + ", requests=" + + mNetworkRequests.size(); + } +} diff --git a/common/device/android/net/NetworkFactoryLegacyImpl.java b/common/device/android/net/NetworkFactoryLegacyImpl.java new file mode 100644 index 00000000..6dd0adb0 --- /dev/null +++ b/common/device/android/net/NetworkFactoryLegacyImpl.java @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.annotation.NonNull; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * A NetworkFactory is an entity that creates NetworkAgent objects. + * The bearers register with ConnectivityService using {@link #register} and + * their factory will start receiving scored NetworkRequests. NetworkRequests + * can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by + * overridden function. All of these can be dynamic - changing NetworkCapabilities + * or score forces re-evaluation of all current requests. + * + * If any requests pass the filter some overrideable functions will be called. + * If the bearer only cares about very simple start/stopNetwork callbacks, those + * functions can be overridden. If the bearer needs more interaction, it can + * override addNetworkRequest and removeNetworkRequest which will give it each + * request that passes their current filters. + * @hide + **/ +// TODO(b/187083878): factor out common code between this and NetworkFactoryImpl +class NetworkFactoryLegacyImpl extends Handler + implements NetworkFactoryShim { + private static final boolean DBG = NetworkFactory.DBG; + private static final boolean VDBG = NetworkFactory.VDBG; + + // TODO(b/187082970): Replace CMD_* with Handler.post(() -> { ... }) since all the CMDs do is to + // run the tasks asynchronously on the Handler thread. + + /** + * Pass a network request to the bearer. If the bearer believes it can + * satisfy the request it should connect to the network and create a + * NetworkAgent. Once the NetworkAgent is fully functional it will + * register itself with ConnectivityService using registerNetworkAgent. + * If the bearer cannot immediately satisfy the request (no network, + * user disabled the radio, lower-scored network) it should remember + * any NetworkRequests it may be able to satisfy in the future. It may + * disregard any that it will never be able to service, for example + * those requiring a different bearer. + * msg.obj = NetworkRequest + * msg.arg1 = score - the score of the network currently satisfying this + * request. If this bearer knows in advance it cannot + * exceed this score it should not try to connect, holding the request + * for the future. + * Note that subsequent events may give a different (lower + * or higher) score for this request, transmitted to each + * NetworkFactory through additional CMD_REQUEST_NETWORK msgs + * with the same NetworkRequest but an updated score. + * Also, network conditions may change for this bearer + * allowing for a better score in the future. + * msg.arg2 = the ID of the NetworkProvider currently responsible for the + * NetworkAgent handling this request, or NetworkProvider.ID_NONE if none. + */ + public static final int CMD_REQUEST_NETWORK = 1; + + /** + * Cancel a network request + * msg.obj = NetworkRequest + */ + public static final int CMD_CANCEL_REQUEST = 2; + + /** + * Internally used to set our best-guess score. + * msg.arg1 = new score + */ + private static final int CMD_SET_SCORE = 3; + + /** + * Internally used to set our current filter for coarse bandwidth changes with + * technology changes. + * msg.obj = new filter + */ + private static final int CMD_SET_FILTER = 4; + + final Context mContext; + final NetworkFactory mParent; + + private final Map<NetworkRequest, NetworkRequestInfo> mNetworkRequests = + new LinkedHashMap<>(); + + private int mScore; + NetworkCapabilities mCapabilityFilter; + + NetworkProvider mProvider = null; + + NetworkFactoryLegacyImpl(NetworkFactory parent, Looper looper, Context context, + NetworkCapabilities filter) { + super(looper); + mParent = parent; + mContext = context; + mCapabilityFilter = filter; + } + + /* Registers this NetworkFactory with the system. May only be called once per factory. */ + @Override public void register(final String logTag) { + if (mProvider != null) { + throw new IllegalStateException("A NetworkFactory must only be registered once"); + } + if (DBG) mParent.log("Registering NetworkFactory"); + + mProvider = new NetworkProvider(mContext, NetworkFactoryLegacyImpl.this.getLooper(), + logTag) { + @Override + public void onNetworkRequested(@NonNull NetworkRequest request, int score, + int servingProviderId) { + handleAddRequest(request, score, servingProviderId); + } + + @Override + public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) { + handleRemoveRequest(request); + } + }; + + ((ConnectivityManager) mContext.getSystemService( + Context.CONNECTIVITY_SERVICE)).registerNetworkProvider(mProvider); + } + + /** Unregisters this NetworkFactory. After this call, the object can no longer be used. */ + @Override public void terminate() { + if (mProvider == null) { + throw new IllegalStateException("This NetworkFactory was never registered"); + } + if (DBG) mParent.log("Unregistering NetworkFactory"); + + ((ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE)) + .unregisterNetworkProvider(mProvider); + + // Remove all pending messages, since this object cannot be reused. Any message currently + // being processed will continue to run. + removeCallbacksAndMessages(null); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case CMD_REQUEST_NETWORK: { + handleAddRequest((NetworkRequest) msg.obj, msg.arg1, msg.arg2); + break; + } + case CMD_CANCEL_REQUEST: { + handleRemoveRequest((NetworkRequest) msg.obj); + break; + } + case CMD_SET_SCORE: { + handleSetScore(msg.arg1); + break; + } + case CMD_SET_FILTER: { + handleSetFilter((NetworkCapabilities) msg.obj); + break; + } + } + } + + private static class NetworkRequestInfo { + public final NetworkRequest request; + public int score; + public boolean requested; // do we have a request outstanding, limited by score + public int providerId; + + NetworkRequestInfo(NetworkRequest request, int score, int providerId) { + this.request = request; + this.score = score; + this.requested = false; + this.providerId = providerId; + } + + @Override + public String toString() { + return "{" + request + ", score=" + score + ", requested=" + requested + "}"; + } + } + + /** + * Add a NetworkRequest that the bearer may want to attempt to satisfy. + * @see #CMD_REQUEST_NETWORK + * + * @param request the request to handle. + * @param score the score of the NetworkAgent currently satisfying this request. + * @param servingProviderId the ID of the NetworkProvider that created the NetworkAgent + * currently satisfying this request. + */ + @VisibleForTesting + protected void handleAddRequest(NetworkRequest request, int score, int servingProviderId) { + NetworkRequestInfo n = mNetworkRequests.get(request); + if (n == null) { + if (DBG) { + mParent.log("got request " + request + " with score " + score + + " and providerId " + servingProviderId); + } + n = new NetworkRequestInfo(request, score, servingProviderId); + mNetworkRequests.put(n.request, n); + } else { + if (VDBG) { + mParent.log("new score " + score + " for existing request " + request + + " and providerId " + servingProviderId); + } + n.score = score; + n.providerId = servingProviderId; + } + if (VDBG) mParent.log(" my score=" + mScore + ", my filter=" + mCapabilityFilter); + + evalRequest(n); + } + + private void handleRemoveRequest(NetworkRequest request) { + NetworkRequestInfo n = mNetworkRequests.get(request); + if (n != null) { + mNetworkRequests.remove(request); + if (n.requested) mParent.releaseNetworkFor(n.request); + } + } + + private void handleSetScore(int score) { + mScore = score; + evalRequests(); + } + + private void handleSetFilter(NetworkCapabilities netCap) { + mCapabilityFilter = netCap; + evalRequests(); + } + + /** + * Overridable function to provide complex filtering. + * Called for every request every time a new NetworkRequest is seen + * and whenever the filterScore or filterNetworkCapabilities change. + * + * acceptRequest can be overridden to provide complex filter behavior + * for the incoming requests + * + * For output, this class will call {@link NetworkFactory#needNetworkFor} and + * {@link NetworkFactory#releaseNetworkFor} for every request that passes the filters. + * If you don't need to see every request, you can leave the base + * implementations of those two functions and instead override + * {@link NetworkFactory#startNetwork} and {@link NetworkFactory#stopNetwork}. + * + * If you want to see every score fluctuation on every request, set + * your score filter to a very high number and watch {@link NetworkFactory#needNetworkFor}. + * + * @return {@code true} to accept the request. + */ + public boolean acceptRequest(NetworkRequest request) { + return mParent.acceptRequest(request); + } + + private void evalRequest(NetworkRequestInfo n) { + if (VDBG) { + mParent.log("evalRequest"); + mParent.log(" n.requests = " + n.requested); + mParent.log(" n.score = " + n.score); + mParent.log(" mScore = " + mScore); + mParent.log(" request.providerId = " + n.providerId); + mParent.log(" mProvider.id = " + mProvider.getProviderId()); + } + if (shouldNeedNetworkFor(n)) { + if (VDBG) mParent.log(" needNetworkFor"); + mParent.needNetworkFor(n.request); + n.requested = true; + } else if (shouldReleaseNetworkFor(n)) { + if (VDBG) mParent.log(" releaseNetworkFor"); + mParent.releaseNetworkFor(n.request); + n.requested = false; + } else { + if (VDBG) mParent.log(" done"); + } + } + + private boolean shouldNeedNetworkFor(NetworkRequestInfo n) { + // If this request is already tracked, it doesn't qualify for need + return !n.requested + // If the score of this request is higher or equal to that of this factory and some + // other factory is responsible for it, then this factory should not track the request + // because it has no hope of satisfying it. + && (n.score < mScore || n.providerId == mProvider.getProviderId()) + // If this factory can't satisfy the capability needs of this request, then it + // should not be tracked. + && n.request.canBeSatisfiedBy(mCapabilityFilter) + // Finally if the concrete implementation of the factory rejects the request, then + // don't track it. + && acceptRequest(n.request); + } + + private boolean shouldReleaseNetworkFor(NetworkRequestInfo n) { + // Don't release a request that's not tracked. + return n.requested + // The request should be released if it can't be satisfied by this factory. That + // means either of the following conditions are met : + // - Its score is too high to be satisfied by this factory and it's not already + // assigned to the factory + // - This factory can't satisfy the capability needs of the request + // - The concrete implementation of the factory rejects the request + && ((n.score > mScore && n.providerId != mProvider.getProviderId()) + || !n.request.canBeSatisfiedBy(mCapabilityFilter) + || !acceptRequest(n.request)); + } + + private void evalRequests() { + for (NetworkRequestInfo n : mNetworkRequests.values()) { + evalRequest(n); + } + } + + /** + * Post a command, on this NetworkFactory Handler, to re-evaluate all + * outstanding requests. Can be called from a factory implementation. + */ + @Override public void reevaluateAllRequests() { + post(this::evalRequests); + } + + /** + * Can be called by a factory to release a request as unfulfillable: the request will be + * removed, and the caller will get a + * {@link ConnectivityManager.NetworkCallback#onUnavailable()} callback after this function + * returns. + * + * Note: this should only be called by factory which KNOWS that it is the ONLY factory which + * is able to fulfill this request! + */ + @Override public void releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r) { + post(() -> { + if (DBG) mParent.log("releaseRequestAsUnfulfillableByAnyFactory: " + r); + final NetworkProvider provider = mProvider; + if (provider == null) { + mParent.log("Ignoring attempt to release unregistered request as unfulfillable"); + return; + } + provider.declareNetworkRequestUnfulfillable(r); + }); + } + + @Override public void setScoreFilter(int score) { + sendMessage(obtainMessage(CMD_SET_SCORE, score, 0)); + } + + @Override public void setScoreFilter(@NonNull final NetworkScore score) { + setScoreFilter(score.getLegacyInt()); + } + + @Override public void setCapabilityFilter(NetworkCapabilities netCap) { + sendMessage(obtainMessage(CMD_SET_FILTER, new NetworkCapabilities(netCap))); + } + + @Override public int getRequestCount() { + return mNetworkRequests.size(); + } + + /* TODO: delete when all callers have migrated to NetworkProvider IDs. */ + @Override public int getSerialNumber() { + return mProvider.getProviderId(); + } + + @Override public NetworkProvider getProvider() { + return mProvider; + } + + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { + writer.println(toString()); + for (NetworkRequestInfo n : mNetworkRequests.values()) { + writer.println(" " + n); + } + } + + @Override public String toString() { + return "providerId=" + + mProvider.getProviderId() + ", ScoreFilter=" + + mScore + ", Filter=" + mCapabilityFilter + ", requests=" + + mNetworkRequests.size(); + } +} diff --git a/common/device/android/net/NetworkFactoryShim.java b/common/device/android/net/NetworkFactoryShim.java new file mode 100644 index 00000000..dfbb5ec1 --- /dev/null +++ b/common/device/android/net/NetworkFactoryShim.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Looper; +import android.os.Message; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * Extract an interface for multiple implementation of {@link NetworkFactory}, depending on the SDK + * version. + * + * Known implementations: + * - {@link NetworkFactoryImpl}: For Android S+ + * - {@link NetworkFactoryLegacyImpl}: For Android R- + * + * @hide + */ +interface NetworkFactoryShim { + void register(String logTag); + + default void registerIgnoringScore(String logTag) { + throw new UnsupportedOperationException(); + } + + void terminate(); + + void releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r); + + void reevaluateAllRequests(); + + void setScoreFilter(int score); + + void setScoreFilter(@NonNull NetworkScore score); + + void setCapabilityFilter(NetworkCapabilities netCap); + + int getRequestCount(); + + int getSerialNumber(); + + NetworkProvider getProvider(); + + void dump(FileDescriptor fd, PrintWriter writer, String[] args); + + // All impls inherit Handler + @VisibleForTesting + Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj); + + Looper getLooper(); +} diff --git a/common/jarjar-rules-shared.txt b/common/jarjar-rules-shared.txt index a7fda7c0..e26999dd 100644 --- a/common/jarjar-rules-shared.txt +++ b/common/jarjar-rules-shared.txt @@ -1,2 +1,3 @@ rule android.annotation.** com.android.net.module.annotation.@1 rule com.android.internal.annotations.** com.android.net.module.annotation.@1 +rule com.android.modules.utils.build.** com.android.net.module.util.build.$1 |