diff options
9 files changed, 485 insertions, 65 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index dbde3862ddb3..0d012264c162 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -74,6 +74,7 @@ package android { field public static final String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION"; field public static final String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE"; field public static final String CONTROL_KEYGUARD_SECURE_NOTIFICATIONS = "android.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS"; + field public static final String CONTROL_OEM_PAID_NETWORK_PREFERENCE = "android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE"; field public static final String CONTROL_VPN = "android.permission.CONTROL_VPN"; field public static final String CREATE_USERS = "android.permission.CREATE_USERS"; field public static final String CRYPT_KEEPER = "android.permission.CRYPT_KEEPER"; @@ -6045,6 +6046,7 @@ package android.net { method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean); + method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi(); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle); method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); @@ -6066,6 +6068,10 @@ package android.net { field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd } + public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener { + method public void onComplete(); + } + @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback { ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback(); method @Deprecated public void onTetheringFailed(); @@ -6423,6 +6429,26 @@ package android.net { ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long); } + public final class OemNetworkPreferences implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getNetworkPreferences(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.OemNetworkPreferences> CREATOR; + field public static final int OEM_NETWORK_PREFERENCE_OEM_PAID = 1; // 0x1 + field public static final int OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK = 2; // 0x2 + field public static final int OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY = 3; // 0x3 + field public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4; // 0x4 + field public static final int OEM_NETWORK_PREFERENCE_UNINITIALIZED = 0; // 0x0 + } + + public static final class OemNetworkPreferences.Builder { + ctor public OemNetworkPreferences.Builder(); + ctor public OemNetworkPreferences.Builder(@NonNull android.net.OemNetworkPreferences); + method @NonNull public android.net.OemNetworkPreferences.Builder addNetworkPreference(@NonNull String, int); + method @NonNull public android.net.OemNetworkPreferences build(); + method @NonNull public android.net.OemNetworkPreferences.Builder clearNetworkPreference(@NonNull String); + } + public abstract class QosCallback { ctor public QosCallback(); method public void onError(@NonNull android.net.QosCallbackException); diff --git a/core/java/android/net/IOnSetOemNetworkPreferenceListener.aidl b/core/java/android/net/IOnSetOemNetworkPreferenceListener.aidl new file mode 100644 index 000000000000..7979afc54f90 --- /dev/null +++ b/core/java/android/net/IOnSetOemNetworkPreferenceListener.aidl @@ -0,0 +1,23 @@ +/** + * + * 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; + +/** @hide */ +oneway interface IOnSetOemNetworkPreferenceListener { + void onComplete(); +} diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java index 5e56164cc82c..b4034556f66e 100644 --- a/core/java/android/net/OemNetworkPreferences.java +++ b/core/java/android/net/OemNetworkPreferences.java @@ -18,6 +18,7 @@ package android.net; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Bundle; import android.os.Parcelable; @@ -29,11 +30,12 @@ import java.util.Map; import java.util.Objects; /** @hide */ +@SystemApi public final class OemNetworkPreferences implements Parcelable { /** - * Use default behavior requesting networks. Equivalent to not setting any preference at all. + * Default in case this value is not set. Using it will result in an error. */ - public static final int OEM_NETWORK_PREFERENCE_DEFAULT = 0; + public static final int OEM_NETWORK_PREFERENCE_UNINITIALIZED = 0; /** * If an unmetered network is available, use it. @@ -45,17 +47,17 @@ public final class OemNetworkPreferences implements Parcelable { /** * If an unmetered network is available, use it. * Otherwise, if a network with the OEM_PAID capability is available, use it. - * Otherwise, the app doesn't get a network. + * Otherwise, the app doesn't get a default network. */ public static final int OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK = 2; /** - * Prefer only NET_CAPABILITY_OEM_PAID networks. + * Use only NET_CAPABILITY_OEM_PAID networks. */ public static final int OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY = 3; /** - * Prefer only NET_CAPABILITY_OEM_PRIVATE networks. + * Use only NET_CAPABILITY_OEM_PRIVATE networks. */ public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4; @@ -95,8 +97,6 @@ public final class OemNetworkPreferences implements Parcelable { /** * Builder used to create {@link OemNetworkPreferences} objects. Specify the preferred Network * to package name mappings. - * - * @hide */ public static final class Builder { private final Bundle mNetworkMappings; @@ -135,7 +135,7 @@ public final class OemNetworkPreferences implements Parcelable { * @return The builder to facilitate chaining. */ @NonNull - public Builder removeNetworkPreference(@NonNull final String packageName) { + public Builder clearNetworkPreference(@NonNull final String packageName) { Objects.requireNonNull(packageName); mNetworkMappings.remove(packageName); return this; @@ -160,7 +160,7 @@ public final class OemNetworkPreferences implements Parcelable { /** @hide */ @IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = { - OEM_NETWORK_PREFERENCE_DEFAULT, + OEM_NETWORK_PREFERENCE_UNINITIALIZED, OEM_NETWORK_PREFERENCE_OEM_PAID, OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK, OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY, @@ -174,12 +174,14 @@ public final class OemNetworkPreferences implements Parcelable { * * @param value int value of OemNetworkPreference * @return string version of OemNetworkPreference + * + * @hide */ @NonNull public static String oemNetworkPreferenceToString(@OemNetworkPreference int value) { switch (value) { - case OEM_NETWORK_PREFERENCE_DEFAULT: - return "OEM_NETWORK_PREFERENCE_DEFAULT"; + case OEM_NETWORK_PREFERENCE_UNINITIALIZED: + return "OEM_NETWORK_PREFERENCE_UNINITIALIZED"; case OEM_NETWORK_PREFERENCE_OEM_PAID: return "OEM_NETWORK_PREFERENCE_OEM_PAID"; case OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK: diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 43c87892c97b..24539b724f70 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1790,6 +1790,12 @@ <permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi @hide Allows an application to manage an automotive device's application network + preference as it relates to OEM_PAID and OEM_PRIVATE capable networks. + <p>Not for use by third-party or privileged applications. --> + <permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE" + android:protectionLevel="signature" /> + <!-- ======================================= --> <!-- Permissions for short range, peripheral networks --> <!-- ======================================= --> diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java index 0976b753e674..a972d9fbe93c 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java @@ -4886,15 +4886,6 @@ public class ConnectivityManager { } } - private void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) { - try { - mService.setOemNetworkPreference(preference); - } catch (RemoteException e) { - Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString()); - throw e.rethrowFromSystemServer(); - } - } - @NonNull private final List<QosCallbackConnection> mQosCallbackConnections = new ArrayList<>(); @@ -5096,4 +5087,60 @@ public class ConnectivityManager { sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST, TYPE_NONE, handler == null ? getDefaultHandler() : new CallbackHandler(handler)); } + + /** + * Listener for {@link #setOemNetworkPreference(OemNetworkPreferences, Executor, + * OnSetOemNetworkPreferenceListener)}. + * @hide + */ + @SystemApi + public interface OnSetOemNetworkPreferenceListener { + /** + * Called when setOemNetworkPreference() successfully completes. + */ + void onComplete(); + } + + /** + * Used by automotive devices to set the network preferences used to direct traffic at an + * application level as per the given OemNetworkPreferences. An example use-case would be an + * automotive OEM wanting to provide connectivity for applications critical to the usage of a + * vehicle via a particular network. + * + * Calling this will overwrite the existing preference. + * + * @param preference {@link OemNetworkPreferences} The application network preference to be set. + * @param executor the executor on which listener will be invoked. + * @param listener {@link OnSetOemNetworkPreferenceListener} optional listener used to + * communicate completion of setOemNetworkPreference(). This will only be + * called once upon successful completion of setOemNetworkPreference(). + * @throws IllegalArgumentException if {@code preference} contains invalid preference values. + * @throws SecurityException if missing the appropriate permissions. + * @throws UnsupportedOperationException if called on a non-automotive device. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) + public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference, + @Nullable @CallbackExecutor final Executor executor, + @Nullable final OnSetOemNetworkPreferenceListener listener) { + Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null"); + if (null != listener) { + Objects.requireNonNull(executor, "Executor must be non-null"); + } + final IOnSetOemNetworkPreferenceListener listenerInternal = listener == null ? null : + new IOnSetOemNetworkPreferenceListener.Stub() { + @Override + public void onComplete() { + executor.execute(listener::onComplete); + } + }; + + try { + mService.setOemNetworkPreference(preference, listenerInternal); + } catch (RemoteException e) { + Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString()); + throw e.rethrowFromSystemServer(); + } + } } diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl index f909d1362550..befd4fb03d71 100644 --- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl +++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl @@ -20,6 +20,7 @@ import android.app.PendingIntent; import android.net.ConnectionInfo; import android.net.ConnectivityDiagnosticsManager; import android.net.IConnectivityDiagnosticsCallback; +import android.net.IOnSetOemNetworkPreferenceListener; import android.net.IQosCallback; import android.net.ISocketKeepaliveCallback; import android.net.LinkProperties; @@ -245,5 +246,6 @@ interface IConnectivityManager void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback); void unregisterQosCallback(in IQosCallback callback); - void setOemNetworkPreference(in OemNetworkPreferences preference); + void setOemNetworkPreference(in OemNetworkPreferences preference, + in IOnSetOemNetworkPreferenceListener listener); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 277152d82c34..1012875a41bd 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -47,6 +47,8 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; +import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -95,6 +97,7 @@ import android.net.INetworkMonitor; import android.net.INetworkMonitorCallbacks; import android.net.INetworkPolicyListener; import android.net.INetworkStatsService; +import android.net.IOnSetOemNetworkPreferenceListener; import android.net.IQosCallback; import android.net.ISocketKeepaliveCallback; import android.net.InetAddresses; @@ -571,6 +574,12 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_SET_REQUIRE_VPN_FOR_UIDS = 47; /** + * used internally when setting the default networks for OemNetworkPreferences. + * obj = OemNetworkPreferences + */ + private static final int EVENT_SET_OEM_NETWORK_PREFERENCE = 48; + + /** * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification * should be shown. */ @@ -1039,10 +1048,10 @@ public class ConnectivityService extends IConnectivityManager.Stub mMetricsLog = logger; mNetworkRanker = new NetworkRanker(); - final NetworkRequest defaultInternetRequest = createDefaultInternetRequestForTransport( - -1, NetworkRequest.Type.REQUEST); - mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder(), - null /* attributionTag */); + final NetworkRequest defaultInternetRequest = createDefaultRequest(); + mDefaultRequest = new NetworkRequestInfo( + defaultInternetRequest, null, new Binder(), + null /* attributionTags */); mNetworkRequests.put(defaultInternetRequest, mDefaultRequest); mDefaultNetworkRequests.add(mDefaultRequest); mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest); @@ -1256,15 +1265,25 @@ public class ConnectivityService extends IConnectivityManager.Stub return netCap; } + private NetworkRequest createDefaultRequest() { + return createDefaultInternetRequestForTransport( + TYPE_NONE, NetworkRequest.Type.REQUEST); + } + private NetworkRequest createDefaultInternetRequestForTransport( int transportType, NetworkRequest.Type type) { final NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName()); - if (transportType > -1) { + if (transportType > TYPE_NONE) { netCap.addTransportType(transportType); } + return createNetworkRequest(type, netCap); + } + + private NetworkRequest createNetworkRequest( + NetworkRequest.Type type, NetworkCapabilities netCap) { return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type); } @@ -1314,7 +1333,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (enable) { handleRegisterNetworkRequest(new NetworkRequestInfo( - null, networkRequest, new Binder(), null /* attributionTag */)); + networkRequest, null, new Binder(), + null /* attributionTags */)); } else { handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID, /* callOnUnavailable */ false); @@ -2292,6 +2312,12 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } + private void enforceOemNetworkPreferencesPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE, + "ConnectivityService"); + } + private boolean checkNetworkStackPermission() { return checkAnyPermissionOf( android.Manifest.permission.NETWORK_STACK, @@ -2634,6 +2660,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } pw.println(); + pw.print("Current per-app default networks: "); + pw.increaseIndent(); + dumpPerAppNetworkPreferences(pw); + pw.decreaseIndent(); + pw.println(); + pw.println("Current Networks:"); pw.increaseIndent(); dumpNetworks(pw); @@ -2754,6 +2786,40 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void dumpPerAppNetworkPreferences(IndentingPrintWriter pw) { + pw.println("Per-App Network Preference:"); + pw.increaseIndent(); + if (0 == mOemNetworkPreferences.getNetworkPreferences().size()) { + pw.println("none"); + } else { + pw.println(mOemNetworkPreferences.toString()); + } + pw.decreaseIndent(); + + for (final NetworkRequestInfo defaultRequest : mDefaultNetworkRequests) { + if (mDefaultRequest == defaultRequest) { + continue; + } + + final boolean isActive = null != defaultRequest.getSatisfier(); + pw.println("Is per-app network active:"); + pw.increaseIndent(); + pw.println(isActive); + if (isActive) { + pw.println("Active network: " + defaultRequest.getSatisfier().network.netId); + } + pw.println("Tracked UIDs:"); + pw.increaseIndent(); + if (0 == defaultRequest.mRequests.size()) { + pw.println("none, this should never occur."); + } else { + pw.println(defaultRequest.mRequests.get(0).networkCapabilities.getUids()); + } + pw.decreaseIndent(); + pw.decreaseIndent(); + } + } + private void dumpNetworkRequests(IndentingPrintWriter pw) { for (NetworkRequestInfo nri : requestsSortedById()) { pw.println(nri.toString()); @@ -3586,29 +3652,38 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void handleRegisterNetworkRequest(@NonNull final NetworkRequestInfo nri) { + handleRegisterNetworkRequest(Collections.singletonList(nri)); + } + + private void handleRegisterNetworkRequest(@NonNull final List<NetworkRequestInfo> nris) { ensureRunningOnConnectivityServiceThread(); - mNetworkRequestInfoLogs.log("REGISTER " + nri); - for (final NetworkRequest req : nri.mRequests) { - mNetworkRequests.put(req, nri); - if (req.isListen()) { - for (final NetworkAgentInfo network : mNetworkAgentInfos) { - if (req.networkCapabilities.hasSignalStrength() - && network.satisfiesImmutableCapabilitiesOf(req)) { - updateSignalStrengthThresholds(network, "REGISTER", req); + for (final NetworkRequestInfo nri : nris) { + mNetworkRequestInfoLogs.log("REGISTER " + nri); + for (final NetworkRequest req : nri.mRequests) { + mNetworkRequests.put(req, nri); + if (req.isListen()) { + for (final NetworkAgentInfo network : mNetworkAgentInfos) { + if (req.networkCapabilities.hasSignalStrength() + && network.satisfiesImmutableCapabilitiesOf(req)) { + updateSignalStrengthThresholds(network, "REGISTER", req); + } } } } } + rematchAllNetworksAndRequests(); - // If the nri is satisfied, return as its score has already been sent if needed. - if (nri.isBeingSatisfied()) { - return; - } + for (final NetworkRequestInfo nri : nris) { + // If the nri is satisfied, return as its score has already been sent if needed. + if (nri.isBeingSatisfied()) { + return; + } - // As this request was not satisfied on rematch and thus never had any scores sent to the - // factories, send null now for each request of type REQUEST. - for (final NetworkRequest req : nri.mRequests) { - if (req.isRequest()) sendUpdatedScoreToFactories(req, null); + // As this request was not satisfied on rematch and thus never had any scores sent to + // the factories, send null now for each request of type REQUEST. + for (final NetworkRequest req : nri.mRequests) { + if (req.isRequest()) sendUpdatedScoreToFactories(req, null); + } } } @@ -3781,6 +3856,7 @@ public class ConnectivityService extends IConnectivityManager.Stub removeListenRequestFromNetworks(req); } } + mDefaultNetworkRequests.remove(nri); mNetworkRequestCounter.decrementCount(nri.mUid); mNetworkRequestInfoLogs.log("RELEASE " + nri); @@ -4419,6 +4495,16 @@ public class ConnectivityService extends IConnectivityManager.Stub case EVENT_SET_REQUIRE_VPN_FOR_UIDS: handleSetRequireVpnForUids(toBool(msg.arg1), (UidRange[]) msg.obj); break; + case EVENT_SET_OEM_NETWORK_PREFERENCE: + final Pair<OemNetworkPreferences, IOnSetOemNetworkPreferenceListener> arg = + (Pair<OemNetworkPreferences, + IOnSetOemNetworkPreferenceListener>) msg.obj; + try { + handleSetOemNetworkPreference(arg.first, arg.second); + } catch (RemoteException e) { + loge("handleMessage.EVENT_SET_OEM_NETWORK_PREFERENCE failed", e); + } + break; } } } @@ -5551,10 +5637,12 @@ public class ConnectivityService extends IConnectivityManager.Stub final PendingIntent mPendingIntent; boolean mPendingIntentSent; + @Nullable + final Messenger mMessenger; + @Nullable private final IBinder mBinder; final int mPid; final int mUid; - final Messenger messenger; @Nullable final String mCallingAttributionTag; @@ -5570,12 +5658,17 @@ public class ConnectivityService extends IConnectivityManager.Stub return uids; } - NetworkRequestInfo(NetworkRequest r, PendingIntent pi, + NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final PendingIntent pi, @Nullable String callingAttributionTag) { + this(Collections.singletonList(r), pi, callingAttributionTag); + } + + NetworkRequestInfo(@NonNull final List<NetworkRequest> r, + @Nullable final PendingIntent pi, @Nullable String callingAttributionTag) { mRequests = initializeRequests(r); ensureAllNetworkRequestsHaveType(mRequests); mPendingIntent = pi; - messenger = null; + mMessenger = null; mBinder = null; mPid = getCallingPid(); mUid = mDeps.getCallingUid(); @@ -5583,11 +5676,16 @@ public class ConnectivityService extends IConnectivityManager.Stub mCallingAttributionTag = callingAttributionTag; } - NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, - @Nullable String callingAttributionTag) { + NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final Messenger m, + @Nullable final IBinder binder, @Nullable String callingAttributionTag) { + this(Collections.singletonList(r), m, binder, callingAttributionTag); + } + + NetworkRequestInfo(@NonNull final List<NetworkRequest> r, @Nullable final Messenger m, + @Nullable final IBinder binder, @Nullable String callingAttributionTag) { super(); - messenger = m; mRequests = initializeRequests(r); + mMessenger = m; ensureAllNetworkRequestsHaveType(mRequests); mBinder = binder; mPid = getCallingPid(); @@ -5603,7 +5701,11 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - NetworkRequestInfo(NetworkRequest r) { + NetworkRequestInfo(@NonNull final NetworkRequest r) { + this(Collections.singletonList(r)); + } + + NetworkRequestInfo(@NonNull final List<NetworkRequest> r) { this(r, null /* pi */, null /* callingAttributionTag */); } @@ -5618,9 +5720,10 @@ public class ConnectivityService extends IConnectivityManager.Stub return mRequests.size() > 1; } - private List<NetworkRequest> initializeRequests(NetworkRequest r) { - final ArrayList<NetworkRequest> tempRequests = new ArrayList<>(); - tempRequests.add(new NetworkRequest(r)); + private List<NetworkRequest> initializeRequests(List<NetworkRequest> r) { + // Creating a defensive copy to prevent the sender from modifying the list being + // reflected in the return value of this method. + final List<NetworkRequest> tempRequests = new ArrayList<>(r); return Collections.unmodifiableList(tempRequests); } @@ -5804,7 +5907,7 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType, nextNetworkRequestId(), reqType); NetworkRequestInfo nri = - new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag); + new NetworkRequestInfo(networkRequest, messenger, binder, callingAttributionTag); if (DBG) log("requestNetwork for " + nri); // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were @@ -5970,7 +6073,7 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); NetworkRequestInfo nri = - new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag); + new NetworkRequestInfo(networkRequest, messenger, binder, callingAttributionTag); if (VDBG) log("listenForNetwork for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri)); @@ -6098,13 +6201,20 @@ public class ConnectivityService extends IConnectivityManager.Stub @GuardedBy("mBlockedAppUids") private final HashSet<Integer> mBlockedAppUids = new HashSet<>(); + // Current OEM network preferences. + @NonNull + private OemNetworkPreferences mOemNetworkPreferences = + new OemNetworkPreferences.Builder().build(); + // The always-on request for an Internet-capable network that apps without a specific default // fall back to. + @VisibleForTesting @NonNull - private final NetworkRequestInfo mDefaultRequest; + final NetworkRequestInfo mDefaultRequest; // Collection of NetworkRequestInfo's used for default networks. + @VisibleForTesting @NonNull - private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>(); + final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>(); private boolean isPerAppDefaultRequest(@NonNull final NetworkRequestInfo nri) { return (mDefaultNetworkRequests.contains(nri) && mDefaultRequest != nri); @@ -7181,7 +7291,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private void callCallbackForRequest(@NonNull final NetworkRequestInfo nri, @NonNull final NetworkAgentInfo networkAgent, final int notificationType, final int arg1) { - if (nri.messenger == null) { + if (nri.mMessenger == null) { // Default request has no msgr. Also prevents callbacks from being invoked for // NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks // are Type.LISTEN, but should not have NetworkCallbacks invoked. @@ -7250,7 +7360,7 @@ public class ConnectivityService extends IConnectivityManager.Stub String notification = ConnectivityManager.getCallbackName(notificationType); log("sending notification " + notification + " for " + nrForCallback); } - nri.messenger.send(msg); + nri.mMessenger.send(msg); } catch (RemoteException e) { // may occur naturally in the race of binder death. loge("RemoteException caught trying to send a callback msg for " + nrForCallback); @@ -9205,9 +9315,212 @@ public class ConnectivityService extends IConnectivityManager.Stub mQosCallbackTracker.unregisterCallback(callback); } + private void enforceAutomotiveDevice() { + final boolean isAutomotiveDevice = + mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); + if (!isAutomotiveDevice) { + throw new UnsupportedOperationException( + "setOemNetworkPreference() is only available on automotive devices."); + } + } + + /** + * Used by automotive devices to set the network preferences used to direct traffic at an + * application level as per the given OemNetworkPreferences. An example use-case would be an + * automotive OEM wanting to provide connectivity for applications critical to the usage of a + * vehicle via a particular network. + * + * Calling this will overwrite the existing preference. + * + * @param preference {@link OemNetworkPreferences} The application network preference to be set. + * @param listener {@link ConnectivityManager.OnSetOemNetworkPreferenceListener} Listener used + * to communicate completion of setOemNetworkPreference(); + */ @Override - public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) { - // TODO http://b/176495594 track multiple default networks with networkPreferences - if (DBG) log("setOemNetworkPreference() called with: " + preference.toString()); + public void setOemNetworkPreference( + @NonNull final OemNetworkPreferences preference, + @Nullable final IOnSetOemNetworkPreferenceListener listener) { + + enforceAutomotiveDevice(); + enforceOemNetworkPreferencesPermission(); + + Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null"); + validateOemNetworkPreferences(preference); + mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_OEM_NETWORK_PREFERENCE, + new Pair<>(preference, listener))); + } + + private void validateOemNetworkPreferences(@NonNull OemNetworkPreferences preference) { + for (@OemNetworkPreferences.OemNetworkPreference final int pref + : preference.getNetworkPreferences().values()) { + if (OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED == pref) { + final String msg = "OEM_NETWORK_PREFERENCE_UNINITIALIZED is an invalid value."; + throw new IllegalArgumentException(msg); + } + } + } + + private void handleSetOemNetworkPreference( + @NonNull final OemNetworkPreferences preference, + @NonNull final IOnSetOemNetworkPreferenceListener listener) throws RemoteException { + Objects.requireNonNull(preference, "OemNetworkPreferences must be non-null"); + if (DBG) { + log("set OEM network preferences :" + preference.toString()); + } + final List<NetworkRequestInfo> nris = + new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference); + updateDefaultNetworksForOemNetworkPreference(nris); + mOemNetworkPreferences = preference; + // TODO http://b/176496396 persist data to shared preferences. + + if (null != listener) { + listener.onComplete(); + } + } + + private void updateDefaultNetworksForOemNetworkPreference( + @NonNull final List<NetworkRequestInfo> nris) { + ensureRunningOnConnectivityServiceThread(); + clearNonDefaultNetworkAgents(); + addDefaultNetworkRequests(nris); + } + + private void clearNonDefaultNetworkAgents() { + // Copy mDefaultNetworkRequests to iterate and remove elements from it in + // handleRemoveNetworkRequest() without getting a ConcurrentModificationException. + final NetworkRequestInfo[] nris = + mDefaultNetworkRequests.toArray(new NetworkRequestInfo[0]); + for (final NetworkRequestInfo nri : nris) { + if (mDefaultRequest != nri) { + handleRemoveNetworkRequest(nri); + } + } + } + + private void addDefaultNetworkRequests(@NonNull final List<NetworkRequestInfo> nris) { + mDefaultNetworkRequests.addAll(nris); + handleRegisterNetworkRequest(nris); + } + + /** + * Class used to generate {@link NetworkRequestInfo} based off of {@link OemNetworkPreferences}. + */ + @VisibleForTesting + final class OemNetworkRequestFactory { + List<NetworkRequestInfo> createNrisFromOemNetworkPreferences( + @NonNull final OemNetworkPreferences preference) { + final List<NetworkRequestInfo> nris = new ArrayList<>(); + final SparseArray<Set<Integer>> uids = + createUidsFromOemNetworkPreferences(preference); + for (int i = 0; i < uids.size(); i++) { + final int key = uids.keyAt(i); + final Set<Integer> value = uids.valueAt(i); + final NetworkRequestInfo nri = createNriFromOemNetworkPreferences(key, value); + // No need to add an nri without any requests. + if (0 == nri.mRequests.size()) { + continue; + } + nris.add(nri); + } + + return nris; + } + + private SparseArray<Set<Integer>> createUidsFromOemNetworkPreferences( + @NonNull final OemNetworkPreferences preference) { + final SparseArray<Set<Integer>> uids = new SparseArray<>(); + final PackageManager pm = mContext.getPackageManager(); + for (final Map.Entry<String, Integer> entry : + preference.getNetworkPreferences().entrySet()) { + @OemNetworkPreferences.OemNetworkPreference final int pref = entry.getValue(); + try { + final int uid = pm.getApplicationInfo(entry.getKey(), 0).uid; + if (!uids.contains(pref)) { + uids.put(pref, new ArraySet<>()); + } + uids.get(pref).add(uid); + } catch (PackageManager.NameNotFoundException e) { + // Although this may seem like an error scenario, it is ok that uninstalled + // packages are sent on a network preference as the system will watch for + // package installations associated with this network preference and update + // accordingly. This is done so as to minimize race conditions on app install. + // TODO b/177092163 add app install watching. + continue; + } + } + return uids; + } + + private NetworkRequestInfo createNriFromOemNetworkPreferences( + @OemNetworkPreferences.OemNetworkPreference final int preference, + @NonNull final Set<Integer> uids) { + final List<NetworkRequest> requests = new ArrayList<>(); + // Requests will ultimately be evaluated by order of insertion therefore it matters. + switch (preference) { + case OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID: + requests.add(createUnmeteredNetworkRequest()); + requests.add(createOemPaidNetworkRequest()); + requests.add(createDefaultRequest()); + break; + case OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK: + requests.add(createUnmeteredNetworkRequest()); + requests.add(createOemPaidNetworkRequest()); + break; + case OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID_ONLY: + requests.add(createOemPaidNetworkRequest()); + break; + case OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY: + requests.add(createOemPrivateNetworkRequest()); + break; + default: + // This should never happen. + throw new IllegalArgumentException("createNriFromOemNetworkPreferences()" + + " called with invalid preference of " + preference); + } + + setOemNetworkRequestUids(requests, uids); + return new NetworkRequestInfo(requests); + } + + private NetworkRequest createUnmeteredNetworkRequest() { + final NetworkCapabilities netcap = createDefaultPerAppNetCap() + .addCapability(NET_CAPABILITY_NOT_METERED) + .addCapability(NET_CAPABILITY_VALIDATED); + return createNetworkRequest(NetworkRequest.Type.LISTEN, netcap); + } + + private NetworkRequest createOemPaidNetworkRequest() { + // NET_CAPABILITY_OEM_PAID is a restricted capability. + final NetworkCapabilities netcap = createDefaultPerAppNetCap() + .addCapability(NET_CAPABILITY_OEM_PAID) + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + return createNetworkRequest(NetworkRequest.Type.REQUEST, netcap); + } + + private NetworkRequest createOemPrivateNetworkRequest() { + // NET_CAPABILITY_OEM_PRIVATE is a restricted capability. + final NetworkCapabilities netcap = createDefaultPerAppNetCap() + .addCapability(NET_CAPABILITY_OEM_PRIVATE) + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + return createNetworkRequest(NetworkRequest.Type.REQUEST, netcap); + } + + private NetworkCapabilities createDefaultPerAppNetCap() { + final NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName()); + return netCap; + } + + private void setOemNetworkRequestUids(@NonNull final List<NetworkRequest> requests, + @NonNull final Set<Integer> uids) { + final Set<UidRange> ranges = new ArraySet<>(); + for (final int uid : uids) { + ranges.add(new UidRange(uid, uid)); + } + for (final NetworkRequest req : requests) { + req.networkCapabilities.setUids(ranges); + } + } } } diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml index 009f817af407..d08b2f8d40dd 100644 --- a/tests/net/AndroidManifest.xml +++ b/tests/net/AndroidManifest.xml @@ -48,6 +48,7 @@ <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" /> <uses-permission android:name="android.permission.NETWORK_FACTORY" /> <uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" /> + <uses-permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java index d232a507454d..fd29a9539de8 100644 --- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java +++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java @@ -40,7 +40,7 @@ import java.util.Map; @SmallTest public class OemNetworkPreferencesTest { - private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_DEFAULT; + private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_UNINITIALIZED; private static final String TEST_PACKAGE = "com.google.apps.contacts"; private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder(); @@ -54,7 +54,7 @@ public class OemNetworkPreferencesTest { @Test public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() { assertThrows(NullPointerException.class, - () -> mBuilder.removeNetworkPreference(null)); + () -> mBuilder.clearNetworkPreference(null)); } @Test @@ -129,7 +129,7 @@ public class OemNetworkPreferencesTest { assertTrue(networkPreferences.containsKey(TEST_PACKAGE)); - mBuilder.removeNetworkPreference(TEST_PACKAGE); + mBuilder.clearNetworkPreference(TEST_PACKAGE); networkPreferences = mBuilder.build().getNetworkPreferences(); assertFalse(networkPreferences.containsKey(TEST_PACKAGE)); |