From f2514125b6d871c2889f53e98afc6bba987cd3e7 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 12 Mar 2021 22:50:57 +0900 Subject: [PATCH 1/3] Store the effective UID in NetworkRequestInfo. This is necessary for privileged components such as JobScheduler to file NetworkCallbacks on behalf of other UIDs. This CL adds the field to NetworkRequestInfo and updates some of the matching code, but does not set the field to anything other than the UID that created the request. Thus, it should cause no behaviour changes. Bug: 165835257 Test: refactoring with no change in behaviour, passes existing tests Change-Id: I21a22ed1b851b8511d36f5202c9eac03e7158d3d --- .../android/server/ConnectivityService.java | 124 +++++++++++------- 1 file changed, 73 insertions(+), 51 deletions(-) diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 6fcb3f74c7..c04050101e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1203,7 +1203,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRanker = new NetworkRanker(); final NetworkRequest defaultInternetRequest = createDefaultRequest(); mDefaultRequest = new NetworkRequestInfo( - defaultInternetRequest, null, + Process.myUid(), defaultInternetRequest, null, new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO, null /* attributionTags */); mNetworkRequests.put(defaultInternetRequest, mDefaultRequest); @@ -1410,8 +1410,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (enable) { handleRegisterNetworkRequest(new NetworkRequestInfo( - networkRequest, null, - new Binder(), + Process.myUid(), networkRequest, null, new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO, null /* attributionTags */)); } else { @@ -1558,7 +1557,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final int requestId = nri.getActiveRequest() != null ? nri.getActiveRequest().requestId : nri.mRequests.get(0).requestId; mNetworkInfoBlockingLogs.log(String.format( - "%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId())); + "%s %d(%d) on netId %d", action, nri.mAsUid, requestId, net.getNetId())); } /** @@ -2073,6 +2072,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private void restrictRequestUidsForCallerAndSetRequestorInfo(NetworkCapabilities nc, int callerUid, String callerPackageName) { if (!checkSettingsPermission()) { + // There is no need to track the effective UID of the request here. If the caller lacks + // the settings permission, the effective UID is the same as the calling ID. nc.setSingleUid(callerUid); } nc.setRequestorUidAndPackageName(callerUid, callerPackageName); @@ -5363,6 +5364,8 @@ public class ConnectivityService extends IConnectivityManager.Stub boolean mPendingIntentSent; @Nullable final Messenger mMessenger; + + // Information about the caller that caused this object to be created. @Nullable private final IBinder mBinder; final int mPid; @@ -5370,6 +5373,13 @@ public class ConnectivityService extends IConnectivityManager.Stub final @NetworkCallback.Flag int mCallbackFlags; @Nullable final String mCallingAttributionTag; + + // Effective UID of this request. This is different from mUid when a privileged process + // files a request on behalf of another UID. This UID is used to determine blocked status, + // UID matching, and so on. mUid above is used for permission checks and to enforce the + // maximum limit of registered callbacks per UID. + final int mAsUid; + // In order to preserve the mapping of NetworkRequest-to-callback when apps register // callbacks using a returned NetworkRequest, the original NetworkRequest needs to be // maintained for keying off of. This is only a concern when the original nri @@ -5397,12 +5407,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return (null == uids) ? new ArraySet<>() : uids; } - NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final PendingIntent pi, - @Nullable String callingAttributionTag) { - this(Collections.singletonList(r), r, pi, callingAttributionTag); + NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r, + @Nullable final PendingIntent pi, @Nullable String callingAttributionTag) { + this(asUid, Collections.singletonList(r), r, pi, callingAttributionTag); } - NetworkRequestInfo(@NonNull final List r, + NetworkRequestInfo(int asUid, @NonNull final List r, @NonNull final NetworkRequest requestForCallback, @Nullable final PendingIntent pi, @Nullable String callingAttributionTag) { ensureAllNetworkRequestsHaveType(r); @@ -5413,6 +5423,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mBinder = null; mPid = getCallingPid(); mUid = mDeps.getCallingUid(); + mAsUid = asUid; mNetworkRequestCounter.incrementCountOrThrow(mUid); /** * Location sensitive data not included in pending intent. Only included in @@ -5422,14 +5433,15 @@ public class ConnectivityService extends IConnectivityManager.Stub mCallingAttributionTag = callingAttributionTag; } - NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final Messenger m, + NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r, @Nullable final Messenger m, @Nullable final IBinder binder, @NetworkCallback.Flag int callbackFlags, @Nullable String callingAttributionTag) { - this(Collections.singletonList(r), r, m, binder, callbackFlags, callingAttributionTag); + this(asUid, Collections.singletonList(r), r, m, binder, callbackFlags, + callingAttributionTag); } - NetworkRequestInfo(@NonNull final List r, + NetworkRequestInfo(int asUid, @NonNull final List r, @NonNull final NetworkRequest requestForCallback, @Nullable final Messenger m, @Nullable final IBinder binder, @NetworkCallback.Flag int callbackFlags, @@ -5442,6 +5454,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mBinder = binder; mPid = getCallingPid(); mUid = mDeps.getCallingUid(); + mAsUid = asUid; mPendingIntent = null; mNetworkRequestCounter.incrementCountOrThrow(mUid); mCallbackFlags = callbackFlags; @@ -5484,18 +5497,19 @@ public class ConnectivityService extends IConnectivityManager.Stub mBinder = nri.mBinder; mPid = nri.mPid; mUid = nri.mUid; + mAsUid = nri.mAsUid; mPendingIntent = nri.mPendingIntent; mNetworkRequestCounter.incrementCountOrThrow(mUid); mCallbackFlags = nri.mCallbackFlags; mCallingAttributionTag = nri.mCallingAttributionTag; } - NetworkRequestInfo(@NonNull final NetworkRequest r) { - this(Collections.singletonList(r)); + NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r) { + this(asUid, Collections.singletonList(r)); } - NetworkRequestInfo(@NonNull final List r) { - this(r, r.get(0), null /* pi */, null /* callingAttributionTag */); + NetworkRequestInfo(int asUid, @NonNull final List r) { + this(asUid, r, r.get(0), null /* pi */, null /* callingAttributionTag */); } // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer @@ -5531,9 +5545,10 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public String toString() { - return "uid/pid:" + mUid + "/" + mPid + " active request Id: " + final String asUidString = (mAsUid == mUid) ? "" : " asUid: " + mAsUid; + return "uid/pid:" + mUid + "/" + mPid + asUidString + " activeRequest: " + (mActiveRequest == null ? null : mActiveRequest.requestId) - + " callback request Id: " + + " callbackRequest: " + mNetworkRequestForCallback.requestId + " " + mRequests + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent) @@ -5658,7 +5673,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // is unused and will be replaced by ones appropriate for the caller. // This allows callers to keep track of the default network for their app. networkCapabilities = copyDefaultNetworkCapabilitiesForUid( - defaultNc, callingUid, callingPackageName); + defaultNc, callingUid, callingUid, callingPackageName); enforceAccessPermission(); break; case TRACK_SYSTEM_DEFAULT: @@ -5710,7 +5725,8 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType, nextNetworkRequestId(), reqType); final NetworkRequestInfo nri = getNriToRegister( - networkRequest, messenger, binder, callbackFlags, callingAttributionTag); + callingUid, networkRequest, messenger, binder, callbackFlags, + callingAttributionTag); if (DBG) log("requestNetwork for " + nri); // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were @@ -5737,25 +5753,27 @@ public class ConnectivityService extends IConnectivityManager.Stub * requests registered to track the default request. If there is currently a per-app default * tracking the app requestor, then we need to create a version of this nri that mirrors that of * the tracking per-app default so that callbacks are sent to the app requestor appropriately. + * @param asUid the uid on behalf of which to file the request. Different from requestorUid + * when a privileged caller is tracking the default network for another uid. * @param nr the network request for the nri. * @param msgr the messenger for the nri. * @param binder the binder for the nri. * @param callingAttributionTag the calling attribution tag for the nri. * @return the nri to register. */ - private NetworkRequestInfo getNriToRegister(@NonNull final NetworkRequest nr, + private NetworkRequestInfo getNriToRegister(final int asUid, @NonNull final NetworkRequest nr, @Nullable final Messenger msgr, @Nullable final IBinder binder, @NetworkCallback.Flag int callbackFlags, @Nullable String callingAttributionTag) { final List requests; if (NetworkRequest.Type.TRACK_DEFAULT == nr.type) { requests = copyDefaultNetworkRequestsForUid( - nr.getRequestorUid(), nr.getRequestorPackageName()); + asUid, nr.getRequestorUid(), nr.getRequestorPackageName()); } else { requests = Collections.singletonList(nr); } return new NetworkRequestInfo( - requests, nr, msgr, binder, callbackFlags, callingAttributionTag); + asUid, requests, nr, msgr, binder, callbackFlags, callingAttributionTag); } private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities, @@ -5836,8 +5854,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.REQUEST); - NetworkRequestInfo nri = - new NetworkRequestInfo(networkRequest, operation, callingAttributionTag); + NetworkRequestInfo nri = new NetworkRequestInfo(callingUid, networkRequest, operation, + callingAttributionTag); if (DBG) log("pendingRequest for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT, nri)); @@ -5904,7 +5922,7 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); NetworkRequestInfo nri = - new NetworkRequestInfo(networkRequest, messenger, binder, callbackFlags, + new NetworkRequestInfo(callingUid, networkRequest, messenger, binder, callbackFlags, callingAttributionTag); if (VDBG) log("listenForNetwork for " + nri); @@ -5929,8 +5947,8 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); - NetworkRequestInfo nri = - new NetworkRequestInfo(networkRequest, operation, callingAttributionTag); + NetworkRequestInfo nri = new NetworkRequestInfo(callingUid, networkRequest, operation, + callingAttributionTag); if (VDBG) log("pendingListenForNetwork for " + nri); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri)); @@ -6080,33 +6098,37 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * Get a copy of the network requests of the default request that is currently tracking the * given uid. + * @param asUid the uid on behalf of which to file the request. Different from requestorUid + * when a privileged caller is tracking the default network for another uid. * @param requestorUid the uid to check the default for. * @param requestorPackageName the requestor's package name. * @return a copy of the default's NetworkRequest that is tracking the given uid. */ @NonNull private List copyDefaultNetworkRequestsForUid( - @NonNull final int requestorUid, @NonNull final String requestorPackageName) { + final int asUid, final int requestorUid, @NonNull final String requestorPackageName) { return copyNetworkRequestsForUid( - getDefaultRequestTrackingUid(requestorUid).mRequests, - requestorUid, requestorPackageName); + getDefaultRequestTrackingUid(asUid).mRequests, + asUid, requestorUid, requestorPackageName); } /** * Copy the given nri's NetworkRequest collection. * @param requestsToCopy the NetworkRequest collection to be copied. + * @param asUid the uid on behalf of which to file the request. Different from requestorUid + * when a privileged caller is tracking the default network for another uid. * @param requestorUid the uid to set on the copied collection. * @param requestorPackageName the package name to set on the copied collection. * @return the copied NetworkRequest collection. */ @NonNull private List copyNetworkRequestsForUid( - @NonNull final List requestsToCopy, @NonNull final int requestorUid, - @NonNull final String requestorPackageName) { + @NonNull final List requestsToCopy, final int asUid, + final int requestorUid, @NonNull final String requestorPackageName) { final List requests = new ArrayList<>(); for (final NetworkRequest nr : requestsToCopy) { requests.add(new NetworkRequest(copyDefaultNetworkCapabilitiesForUid( - nr.networkCapabilities, requestorUid, requestorPackageName), + nr.networkCapabilities, asUid, requestorUid, requestorPackageName), nr.legacyType, nextNetworkRequestId(), nr.type)); } return requests; @@ -6114,17 +6136,17 @@ public class ConnectivityService extends IConnectivityManager.Stub @NonNull private NetworkCapabilities copyDefaultNetworkCapabilitiesForUid( - @NonNull final NetworkCapabilities netCapToCopy, @NonNull final int requestorUid, - @NonNull final String requestorPackageName) { + @NonNull final NetworkCapabilities netCapToCopy, final int asUid, + final int requestorUid, @NonNull final String requestorPackageName) { // These capabilities are for a TRACK_DEFAULT callback, so: // 1. Remove NET_CAPABILITY_VPN, because it's (currently!) the only difference between // mDefaultRequest and a per-UID default request. // TODO: stop depending on the fact that these two unrelated things happen to be the same - // 2. Always set the UIDs to mAsUid. restrictRequestUidsForCallerAndSetRequestorInfo will + // 2. Always set the UIDs to asUid. restrictRequestUidsForCallerAndSetRequestorInfo will // not do this in the case of a privileged application. final NetworkCapabilities netCap = new NetworkCapabilities(netCapToCopy); netCap.removeCapability(NET_CAPABILITY_NOT_VPN); - netCap.setSingleUid(requestorUid); + netCap.setSingleUid(asUid); restrictRequestUidsForCallerAndSetRequestorInfo( netCap, requestorUid, requestorPackageName); return netCap; @@ -8025,9 +8047,9 @@ public class ConnectivityService extends IConnectivityManager.Stub final boolean metered = nai.networkCapabilities.isMetered(); boolean blocked; - blocked = isUidBlockedByVpn(nri.mUid, mVpnBlockedUidRanges); + blocked = isUidBlockedByVpn(nri.mAsUid, mVpnBlockedUidRanges); blocked |= NetworkPolicyManager.isUidBlocked( - mUidBlockedReasons.get(nri.mUid, BLOCKED_REASON_NONE), metered); + mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE), metered); callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0); } @@ -8055,12 +8077,12 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkRequestInfo nri = mNetworkRequests.get(nr); final boolean oldBlocked, newBlocked, oldVpnBlocked, newVpnBlocked; - oldVpnBlocked = isUidBlockedByVpn(nri.mUid, oldBlockedUidRanges); + oldVpnBlocked = isUidBlockedByVpn(nri.mAsUid, oldBlockedUidRanges); newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges) - ? isUidBlockedByVpn(nri.mUid, newBlockedUidRanges) + ? isUidBlockedByVpn(nri.mAsUid, newBlockedUidRanges) : oldVpnBlocked; - final int blockedReasons = mUidBlockedReasons.get(nri.mUid, BLOCKED_REASON_NONE); + final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE); oldBlocked = oldVpnBlocked || NetworkPolicyManager.isUidBlocked( blockedReasons, oldMetered); newBlocked = newVpnBlocked || NetworkPolicyManager.isUidBlocked( @@ -8095,7 +8117,7 @@ public class ConnectivityService extends IConnectivityManager.Stub for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest nr = nai.requestAt(i); NetworkRequestInfo nri = mNetworkRequests.get(nr); - if (nri != null && nri.mUid == uid) { + if (nri != null && nri.mAsUid == uid) { callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg); } } @@ -8860,7 +8882,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // nri is not bound to the death of callback. Instead, callback.bindToDeath() is set in // handleRegisterConnectivityDiagnosticsCallback(). nri will be cleaned up as part of the // callback's binder death. - final NetworkRequestInfo nri = new NetworkRequestInfo(requestWithId); + final NetworkRequestInfo nri = new NetworkRequestInfo(callingUid, requestWithId); final ConnectivityDiagnosticsCallbackInfo cbInfo = new ConnectivityDiagnosticsCallbackInfo(callback, nri, callingPackageName); @@ -9344,7 +9366,7 @@ public class ConnectivityService extends IConnectivityManager.Stub nrs.add(createNetworkRequest(NetworkRequest.Type.REQUEST, pref.capabilities)); nrs.add(createDefaultRequest()); setNetworkRequestUids(nrs, UidRange.fromIntRanges(pref.capabilities.getUids())); - final NetworkRequestInfo nri = new NetworkRequestInfo(nrs); + final NetworkRequestInfo nri = new NetworkRequestInfo(Process.myUid(), nrs); result.add(nri); } return result; @@ -9515,7 +9537,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } // Include this nri if it will be tracked by the new per-app default requests. final boolean isNriGoingToBeTracked = - getDefaultRequestTrackingUid(nri.mUid) != mDefaultRequest; + getDefaultRequestTrackingUid(nri.mAsUid) != mDefaultRequest; if (isNriGoingToBeTracked) { defaultCallbackRequests.add(nri); } @@ -9537,7 +9559,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final ArraySet callbackRequestsToRegister = new ArraySet<>(); for (final NetworkRequestInfo callbackRequest : perAppCallbackRequestsForUpdate) { final NetworkRequestInfo trackingNri = - getDefaultRequestTrackingUid(callbackRequest.mUid); + getDefaultRequestTrackingUid(callbackRequest.mAsUid); // If this nri is not being tracked, the change it back to an untracked nri. if (trackingNri == mDefaultRequest) { @@ -9547,12 +9569,12 @@ public class ConnectivityService extends IConnectivityManager.Stub continue; } - final String requestorPackageName = - callbackRequest.mRequests.get(0).getRequestorPackageName(); + final NetworkRequest request = callbackRequest.mRequests.get(0); callbackRequestsToRegister.add(new NetworkRequestInfo( callbackRequest, copyNetworkRequestsForUid( - trackingNri.mRequests, callbackRequest.mUid, requestorPackageName))); + trackingNri.mRequests, callbackRequest.mAsUid, + request.getRequestorUid(), request.getRequestorPackageName()))); } return callbackRequestsToRegister; } @@ -9656,7 +9678,7 @@ public class ConnectivityService extends IConnectivityManager.Stub ranges.add(new UidRange(uid, uid)); } setNetworkRequestUids(requests, ranges); - return new NetworkRequestInfo(requests); + return new NetworkRequestInfo(Process.myUid(), requests); } private NetworkRequest createUnmeteredNetworkRequest() { From b199b960c79aad23053a0b619f7c305cee3aac44 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 12 Mar 2021 22:48:07 +0900 Subject: [PATCH 2/3] Support calling registerDefaultNetworkCallback for another UID. This is to be used by privileged components (e.g., JobScheduler) to request callbacks about the state of other UIDs on the system. Bug: 165835257 Test: new unit test coverage Change-Id: I29f155710394e58c14fcef488db6271d8d83033a --- .../src/android/net/ConnectivityManager.java | 49 ++++++++-- .../src/android/net/IConnectivityManager.aidl | 2 +- .../android/server/ConnectivityService.java | 18 ++-- .../android/net/ConnectivityManagerTest.java | 37 ++++---- .../server/ConnectivityServiceTest.java | 89 +++++++++++++++++-- 5 files changed, 160 insertions(+), 35 deletions(-) diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java index a5e9f31a5b..1a5299bb00 100644 --- a/framework/src/android/net/ConnectivityManager.java +++ b/framework/src/android/net/ConnectivityManager.java @@ -3695,8 +3695,9 @@ public class ConnectivityManager { private static final HashMap sCallbacks = new HashMap<>(); private static CallbackHandler sCallbackHandler; - private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback, - int timeoutMs, NetworkRequest.Type reqType, int legacyType, CallbackHandler handler) { + private NetworkRequest sendRequestForNetwork(int asUid, NetworkCapabilities need, + NetworkCallback callback, int timeoutMs, NetworkRequest.Type reqType, int legacyType, + CallbackHandler handler) { printStackTrace(); checkCallbackNotNull(callback); Preconditions.checkArgument( @@ -3721,8 +3722,8 @@ public class ConnectivityManager { getAttributionTag()); } else { request = mService.requestNetwork( - need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType, - callbackFlags, callingPackageName, getAttributionTag()); + asUid, need, reqType.ordinal(), messenger, timeoutMs, binder, + legacyType, callbackFlags, callingPackageName, getAttributionTag()); } if (request != null) { sCallbacks.put(request, callback); @@ -3737,6 +3738,12 @@ public class ConnectivityManager { return request; } + private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback, + int timeoutMs, NetworkRequest.Type reqType, int legacyType, CallbackHandler handler) { + return sendRequestForNetwork(Process.INVALID_UID, need, callback, timeoutMs, reqType, + legacyType, handler); + } + /** * Helper function to request a network with a particular legacy type. * @@ -4220,8 +4227,40 @@ public class ConnectivityManager { @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback, @NonNull Handler handler) { + registerDefaultNetworkCallbackAsUid(Process.INVALID_UID, networkCallback, handler); + } + + /** + * Registers to receive notifications about changes in the default network for the specified + * UID. This may be a physical network or a virtual network, such as a VPN that applies to the + * UID. The callbacks will continue to be called until either the application exits or + * {@link #unregisterNetworkCallback(NetworkCallback)} is called. + * + *

To avoid performance issues due to apps leaking callbacks, the system will limit the + * number of outstanding requests to 100 per app (identified by their UID), shared with + * all variants of this method, of {@link #requestNetwork} as well as + * {@link ConnectivityDiagnosticsManager#registerConnectivityDiagnosticsCallback}. + * Requesting a network with this method will count toward this limit. If this limit is + * exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources, + * make sure to unregister the callbacks with + * {@link #unregisterNetworkCallback(NetworkCallback)}. + * + * @param uid the UID for which to track default network changes. + * @param networkCallback The {@link NetworkCallback} that the system will call as the + * UID's default network changes. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @throws RuntimeException if the app already has too many callbacks registered. + * @hide + */ + // TODO: @SystemApi(client=MODULE_LIBRARIES) + @SuppressLint({"ExecutorRegistration", "PairedRegistration"}) + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_SETTINGS}) + public void registerDefaultNetworkCallbackAsUid(int uid, + @NonNull NetworkCallback networkCallback, @NonNull Handler handler) { CallbackHandler cbHandler = new CallbackHandler(handler); - sendRequestForNetwork(null /* NetworkCapabilities need */, networkCallback, 0, + sendRequestForNetwork(uid, null /* need */, networkCallback, 0 /* timeoutMs */, TRACK_DEFAULT, TYPE_NONE, cbHandler); } diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl index 3300fa8fd1..0826922e21 100644 --- a/framework/src/android/net/IConnectivityManager.aidl +++ b/framework/src/android/net/IConnectivityManager.aidl @@ -142,7 +142,7 @@ interface IConnectivityManager in NetworkCapabilities nc, in NetworkScore score, in NetworkAgentConfig config, in int factorySerialNumber); - NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType, + NetworkRequest requestNetwork(int uid, in NetworkCapabilities networkCapabilities, int reqType, in Messenger messenger, int timeoutSec, in IBinder binder, int legacy, int callbackFlags, String callingPackageName, String callingAttributionTag); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index c04050101e..a0bdd7f63c 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -5649,7 +5649,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, + public NetworkRequest requestNetwork(int asUid, NetworkCapabilities networkCapabilities, int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder, int legacyType, int callbackFlags, @NonNull String callingPackageName, @Nullable String callingAttributionTag) { @@ -5661,6 +5661,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } final NetworkCapabilities defaultNc = mDefaultRequest.mRequests.get(0).networkCapabilities; final int callingUid = mDeps.getCallingUid(); + // Privileged callers can track the default network of another UID by passing in a UID. + if (asUid != Process.INVALID_UID) { + enforceSettingsPermission(); + } else { + asUid = callingUid; + } final NetworkRequest.Type reqType; try { reqType = NetworkRequest.Type.values()[reqTypeInt]; @@ -5670,10 +5676,10 @@ public class ConnectivityService extends IConnectivityManager.Stub switch (reqType) { case TRACK_DEFAULT: // If the request type is TRACK_DEFAULT, the passed {@code networkCapabilities} - // is unused and will be replaced by ones appropriate for the caller. - // This allows callers to keep track of the default network for their app. + // is unused and will be replaced by ones appropriate for the UID (usually, the + // calling app). This allows callers to keep track of the default network. networkCapabilities = copyDefaultNetworkCapabilitiesForUid( - defaultNc, callingUid, callingUid, callingPackageName); + defaultNc, asUid, callingUid, callingPackageName); enforceAccessPermission(); break; case TRACK_SYSTEM_DEFAULT: @@ -5725,7 +5731,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType, nextNetworkRequestId(), reqType); final NetworkRequestInfo nri = getNriToRegister( - callingUid, networkRequest, messenger, binder, callbackFlags, + asUid, networkRequest, messenger, binder, callbackFlags, callingAttributionTag); if (DBG) log("requestNetwork for " + nri); @@ -9574,7 +9580,7 @@ public class ConnectivityService extends IConnectivityManager.Stub callbackRequest, copyNetworkRequestsForUid( trackingNri.mRequests, callbackRequest.mAsUid, - request.getRequestorUid(), request.getRequestorPackageName()))); + callbackRequest.mUid, request.getRequestorPackageName()))); } return callbackRequestsToRegister; } diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java index 6fc605e269..36f205b72a 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -64,6 +64,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Messenger; +import android.os.Process; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -219,8 +220,8 @@ public class ConnectivityManagerTest { ArgumentCaptor captor = ArgumentCaptor.forClass(Messenger.class); // register callback - when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), - anyInt(), any(), nullable(String.class))).thenReturn(request); + when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), + anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(request); manager.requestNetwork(request, callback, handler); // callback triggers @@ -247,8 +248,8 @@ public class ConnectivityManagerTest { ArgumentCaptor captor = ArgumentCaptor.forClass(Messenger.class); // register callback - when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), - anyInt(), any(), nullable(String.class))).thenReturn(req1); + when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), + anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req1); manager.requestNetwork(req1, callback, handler); // callback triggers @@ -265,8 +266,8 @@ public class ConnectivityManagerTest { verify(callback, timeout(100).times(0)).onLosing(any(), anyInt()); // callback can be registered again - when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(), - anyInt(), any(), nullable(String.class))).thenReturn(req2); + when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(), + anyInt(), anyInt(), any(), nullable(String.class))).thenReturn(req2); manager.requestNetwork(req2, callback, handler); // callback triggers @@ -289,8 +290,8 @@ public class ConnectivityManagerTest { info.targetSdkVersion = VERSION_CODES.N_MR1 + 1; when(mCtx.getApplicationInfo()).thenReturn(info); - when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(), - any(), nullable(String.class))).thenReturn(request); + when(mService.requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(), anyInt(), + anyInt(), any(), nullable(String.class))).thenReturn(request); Handler handler = new Handler(Looper.getMainLooper()); manager.requestNetwork(request, callback, handler); @@ -357,34 +358,40 @@ public class ConnectivityManagerTest { final NetworkCallback callback = new ConnectivityManager.NetworkCallback(); manager.requestNetwork(request, callback); - verify(mService).requestNetwork(eq(request.networkCapabilities), + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities), eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); // Verify that register network callback does not calls requestNetwork at all. manager.registerNetworkCallback(request, callback); - verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), - anyInt(), any(), any()); + verify(mService, never()).requestNetwork(anyInt(), any(), anyInt(), any(), anyInt(), any(), + anyInt(), anyInt(), any(), any()); verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); + Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + manager.registerDefaultNetworkCallback(callback); - verify(mService).requestNetwork(eq(null), + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null), eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); - Handler handler = new Handler(ConnectivityThread.getInstanceLooper()); + manager.registerDefaultNetworkCallbackAsUid(42, callback, handler); + verify(mService).requestNetwork(eq(42), eq(null), + eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), + eq(testPkgName), eq(testAttributionTag)); + manager.requestBackgroundNetwork(request, handler, callback); - verify(mService).requestNetwork(eq(request.networkCapabilities), + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(request.networkCapabilities), eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); manager.registerSystemDefaultNetworkCallback(callback, handler); - verify(mService).requestNetwork(eq(null), + verify(mService).requestNetwork(eq(Process.INVALID_UID), eq(null), eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(), eq(testPkgName), eq(testAttributionTag)); reset(mService); diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index ed9a44b614..ee17d7525a 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -1451,6 +1451,23 @@ public class ConnectivityServiceTest { }); } + private interface ExceptionalRunnable { + void run() throws Exception; + } + + private void withPermission(String permission, ExceptionalRunnable r) throws Exception { + if (mServiceContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { + r.run(); + return; + } + try { + mServiceContext.setPermission(permission, PERMISSION_GRANTED); + r.run(); + } finally { + mServiceContext.setPermission(permission, PERMISSION_DENIED); + } + } + private static final int PRIMARY_USER = 0; private static final UidRange PRIMARY_UIDRANGE = UidRange.createForUser(UserHandle.of(PRIMARY_USER)); @@ -3813,8 +3830,9 @@ public class ConnectivityServiceTest { NetworkCapabilities networkCapabilities = new NetworkCapabilities(); networkCapabilities.addTransportType(TRANSPORT_WIFI) .setNetworkSpecifier(new MatchAllNetworkSpecifier()); - mService.requestNetwork(networkCapabilities, NetworkRequest.Type.REQUEST.ordinal(), - null, 0, null, ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE, + mService.requestNetwork(Process.INVALID_UID, networkCapabilities, + NetworkRequest.Type.REQUEST.ordinal(), null, 0, null, + ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE, mContext.getPackageName(), getAttributionTag()); }); @@ -4043,7 +4061,7 @@ public class ConnectivityServiceTest { } @Test - public void testRegisterSystemDefaultCallbackRequiresNetworkSettings() throws Exception { + public void testRegisterPrivilegedDefaultCallbacksRequireNetworkSettings() throws Exception { mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); mCellNetworkAgent.connect(false /* validated */); @@ -4052,12 +4070,19 @@ public class ConnectivityServiceTest { assertThrows(SecurityException.class, () -> mCm.registerSystemDefaultNetworkCallback(callback, handler)); callback.assertNoCallback(); + assertThrows(SecurityException.class, + () -> mCm.registerDefaultNetworkCallbackAsUid(APP1_UID, callback, handler)); + callback.assertNoCallback(); mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED); mCm.registerSystemDefaultNetworkCallback(callback, handler); callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); mCm.unregisterNetworkCallback(callback); + + mCm.registerDefaultNetworkCallbackAsUid(APP1_UID, callback, handler); + callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); + mCm.unregisterNetworkCallback(callback); } private void setCaptivePortalMode(int mode) { @@ -7490,6 +7515,10 @@ public class ConnectivityServiceTest { final TestNetworkCallback vpnUidDefaultCallback = new TestNetworkCallback(); registerDefaultNetworkCallbackAsUid(vpnUidDefaultCallback, VPN_UID); + final TestNetworkCallback vpnDefaultCallbackAsUid = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallbackAsUid(VPN_UID, vpnDefaultCallbackAsUid, + new Handler(ConnectivityThread.getInstanceLooper())); + final int uid = Process.myUid(); final int userId = UserHandle.getUserId(uid); final ArrayList allowList = new ArrayList<>(); @@ -7509,6 +7538,7 @@ public class ConnectivityServiceTest { defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); vpnUidCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); vpnUidDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + vpnDefaultCallbackAsUid.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); @@ -7522,6 +7552,7 @@ public class ConnectivityServiceTest { defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); @@ -7537,6 +7568,7 @@ public class ConnectivityServiceTest { defaultCallback.assertNoCallback(); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); // The following requires that the UID of this test package is greater than VPN_UID. This // is always true in practice because a plain AOSP build with no apps installed has almost @@ -7558,6 +7590,7 @@ public class ConnectivityServiceTest { defaultCallback.assertNoCallback(); vpnUidCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -7579,6 +7612,7 @@ public class ConnectivityServiceTest { assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); @@ -7591,6 +7625,7 @@ public class ConnectivityServiceTest { assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -7606,6 +7641,7 @@ public class ConnectivityServiceTest { defaultCallback.assertNoCallback(); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -7618,6 +7654,7 @@ public class ConnectivityServiceTest { defaultCallback.assertNoCallback(); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -7631,6 +7668,7 @@ public class ConnectivityServiceTest { assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertNull(mCm.getActiveNetwork()); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED); @@ -7642,6 +7680,7 @@ public class ConnectivityServiceTest { defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn); vpnUidCallback.assertNoCallback(); // vpnUidCallback has NOT_VPN capability. vpnUidDefaultCallback.assertNoCallback(); // VPN does not apply to VPN_UID + vpnDefaultCallbackAsUid.assertNoCallback(); assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork()); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID)); assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED); @@ -7654,12 +7693,14 @@ public class ConnectivityServiceTest { defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mWiFiNetworkAgent); vpnUidCallback.assertNoCallback(); vpnUidDefaultCallback.assertNoCallback(); + vpnDefaultCallbackAsUid.assertNoCallback(); assertNull(mCm.getActiveNetwork()); mCm.unregisterNetworkCallback(callback); mCm.unregisterNetworkCallback(defaultCallback); mCm.unregisterNetworkCallback(vpnUidCallback); mCm.unregisterNetworkCallback(vpnUidDefaultCallback); + mCm.unregisterNetworkCallback(vpnDefaultCallbackAsUid); } private void setupLegacyLockdownVpn() { @@ -9805,8 +9846,8 @@ public class ConnectivityServiceTest { for (int reqTypeInt : invalidReqTypeInts) { assertThrows("Expect throws for invalid request type " + reqTypeInt, IllegalArgumentException.class, - () -> mService.requestNetwork(nc, reqTypeInt, null, 0, null, - ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE, + () -> mService.requestNetwork(Process.INVALID_UID, nc, reqTypeInt, null, 0, + null, ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE, mContext.getPackageName(), getAttributionTag()) ); } @@ -10377,6 +10418,7 @@ public class ConnectivityServiceTest { mCm.registerDefaultNetworkCallback(mDefaultNetworkCallback); registerDefaultNetworkCallbackAsUid(mProfileDefaultNetworkCallback, TEST_WORK_PROFILE_APP_UID); + // TODO: test using ConnectivityManager#registerDefaultNetworkCallbackAsUid as well. mServiceContext.setPermission( Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED); } @@ -10396,7 +10438,7 @@ public class ConnectivityServiceTest { private void setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest( @OemNetworkPreferences.OemNetworkPreference final int networkPrefToSetup) throws Exception { - final int testPackageNameUid = 123; + final int testPackageNameUid = TEST_PACKAGE_UID; final String testPackageName = "per.app.defaults.package"; setupMultipleDefaultNetworksForOemNetworkPreferenceTest( networkPrefToSetup, testPackageNameUid, testPackageName); @@ -10532,6 +10574,11 @@ public class ConnectivityServiceTest { mCm.registerDefaultNetworkCallback(defaultNetworkCallback); defaultNetworkCallback.assertNoCallback(); + final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); + withPermission(Manifest.permission.NETWORK_SETTINGS, () -> + mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper()))); + // Setup the test process to use networkPref for their default network. setupMultipleDefaultNetworksForOemNetworkPreferenceCurrentUidTest(networkPref); @@ -10542,19 +10589,22 @@ public class ConnectivityServiceTest { null, mEthernetNetworkAgent.getNetwork()); - // At this point with a restricted network used, the available callback should trigger + // At this point with a restricted network used, the available callback should trigger. defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mEthernetNetworkAgent.getNetwork()); + otherUidDefaultCallback.assertNoCallback(); // Now bring down the default network which should trigger a LOST callback. setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); // At this point, with no network is available, the lost callback should trigger defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); // Confirm we can unregister without issues. mCm.unregisterNetworkCallback(defaultNetworkCallback); + mCm.unregisterNetworkCallback(otherUidDefaultCallback); } @Test @@ -10572,6 +10622,11 @@ public class ConnectivityServiceTest { mCm.registerDefaultNetworkCallback(defaultNetworkCallback); defaultNetworkCallback.assertNoCallback(); + final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); + withPermission(Manifest.permission.NETWORK_SETTINGS, () -> + mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper()))); + // Bring up ethernet with OEM_PAID. This will satisfy NET_CAPABILITY_OEM_PAID. // The active nai for the default is null at this point as this is a restricted network. setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, true); @@ -10583,15 +10638,19 @@ public class ConnectivityServiceTest { defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mEthernetNetworkAgent.getNetwork()); + otherUidDefaultCallback.assertNoCallback(); // Now bring down the default network which should trigger a LOST callback. setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); + otherUidDefaultCallback.assertNoCallback(); // At this point, with no network is available, the lost callback should trigger defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); // Confirm we can unregister without issues. mCm.unregisterNetworkCallback(defaultNetworkCallback); + mCm.unregisterNetworkCallback(otherUidDefaultCallback); } @Test @@ -10605,6 +10664,11 @@ public class ConnectivityServiceTest { mCm.registerDefaultNetworkCallback(defaultNetworkCallback); defaultNetworkCallback.assertNoCallback(); + final TestNetworkCallback otherUidDefaultCallback = new TestNetworkCallback(); + withPermission(Manifest.permission.NETWORK_SETTINGS, () -> + mCm.registerDefaultNetworkCallbackAsUid(TEST_PACKAGE_UID, otherUidDefaultCallback, + new Handler(ConnectivityThread.getInstanceLooper()))); + // Setup a process different than the test process to use the default network. This means // that the defaultNetworkCallback won't be tracked by the per-app policy. setupMultipleDefaultNetworksForOemNetworkPreferenceNotCurrentUidTest(networkPref); @@ -10620,6 +10684,9 @@ public class ConnectivityServiceTest { defaultNetworkCallback.assertNoCallback(); assertDefaultNetworkCapabilities(userId /* no networks */); + // The other UID does have access, and gets a callback. + otherUidDefaultCallback.expectAvailableThenValidatedCallbacks(mEthernetNetworkAgent); + // Bring up unrestricted cellular. This should now satisfy the default network. setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true); verifyMultipleDefaultNetworksTracksCorrectly(expectedOemPrefRequestSize, @@ -10627,25 +10694,31 @@ public class ConnectivityServiceTest { mEthernetNetworkAgent.getNetwork()); // At this point with an unrestricted network used, the available callback should trigger + // The other UID is unaffected and remains on the paid network. defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCellNetworkAgent.getNetwork()); assertDefaultNetworkCapabilities(userId, mCellNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); // Now bring down the per-app network. setOemNetworkPreferenceAgentConnected(TRANSPORT_ETHERNET, false); - // Since the callback didn't use the per-app network, no callback should fire. + // Since the callback didn't use the per-app network, only the other UID gets a callback. + // Because the preference specifies no fallback, it does not switch to cellular. defaultNetworkCallback.assertNoCallback(); + otherUidDefaultCallback.expectCallback(CallbackEntry.LOST, mEthernetNetworkAgent); // Now bring down the default network. setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, false); // As this callback was tracking the default, this should now trigger. defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); + otherUidDefaultCallback.assertNoCallback(); // Confirm we can unregister without issues. mCm.unregisterNetworkCallback(defaultNetworkCallback); + mCm.unregisterNetworkCallback(otherUidDefaultCallback); } /** From 3949d6ed96509b8d5ee52e93fa384567b270b424 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 22 Mar 2021 18:23:21 +0900 Subject: [PATCH 3/3] Expose registerDefaultNetworkCallbackAsUid. Bug: 165835257 Test: atest FrameworksNetTests Change-Id: I638ed5cd5273d456919630aba1e22f099df1b36c --- framework/api/module-lib-current.txt | 1 + framework/src/android/net/ConnectivityManager.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt index aa7a0ac465..f32f08daf7 100644 --- a/framework/api/module-lib-current.txt +++ b/framework/api/module-lib-current.txt @@ -11,6 +11,7 @@ package android.net { method @Nullable public android.net.ProxyInfo getGlobalProxy(); method @NonNull public static android.util.Range getIpSecNetIdRange(); method @NonNull public static String getPrivateDnsMode(@NonNull android.content.Context); + method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerDefaultNetworkCallbackAsUid(int, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void setAcceptPartialConnectivity(@NonNull android.net.Network, boolean, boolean); diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java index 1a5299bb00..2e240f947d 100644 --- a/framework/src/android/net/ConnectivityManager.java +++ b/framework/src/android/net/ConnectivityManager.java @@ -4252,7 +4252,7 @@ public class ConnectivityManager { * @throws RuntimeException if the app already has too many callbacks registered. * @hide */ - // TODO: @SystemApi(client=MODULE_LIBRARIES) + @SystemApi(client = MODULE_LIBRARIES) @SuppressLint({"ExecutorRegistration", "PairedRegistration"}) @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,