Merge changes from topics "pans", "pans-permission" am: 68658a18c7
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1561317
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: I72035b6d5d6504cece1df472ae317e865ceb7882
diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java
index 5e56164..b403455 100644
--- a/core/java/android/net/OemNetworkPreferences.java
+++ b/core/java/android/net/OemNetworkPreferences.java
@@ -18,6 +18,7 @@
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.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 @@
/**
* 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 @@
/**
* 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 @@
* @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 @@
/** @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 @@
*
* @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/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 0976b75..a972d9f 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -4886,15 +4886,6 @@
}
}
- 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 @@
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/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index f909d13..befd4fb 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -20,6 +20,7 @@
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 @@
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 277152d..1012875 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_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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
}
+ 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 @@
}
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;
- }
- // 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);
+ rematchAllNetworksAndRequests();
+ 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);
+ }
}
}
@@ -3781,6 +3856,7 @@
removeListenRequestFromNetworks(req);
}
}
+ mDefaultNetworkRequests.remove(nri);
mNetworkRequestCounter.decrementCount(nri.mUid);
mNetworkRequestInfoLogs.log("RELEASE " + nri);
@@ -4419,6 +4495,16 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
@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 @@
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 @@
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 @@
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/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
index d232a50..fd29a95 100644
--- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
@@ -40,7 +40,7 @@
@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 @@
@Test
public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() {
assertThrows(NullPointerException.class,
- () -> mBuilder.removeNetworkPreference(null));
+ () -> mBuilder.clearNetworkPreference(null));
}
@Test
@@ -129,7 +129,7 @@
assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
- mBuilder.removeNetworkPreference(TEST_PACKAGE);
+ mBuilder.clearNetworkPreference(TEST_PACKAGE);
networkPreferences = mBuilder.build().getNetworkPreferences();
assertFalse(networkPreferences.containsKey(TEST_PACKAGE));