Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb83c298 authored by Cody Kesting's avatar Cody Kesting Committed by Android (Google) Code Review
Browse files

Merge "Implement INetworkMonitorCallbacks#notifyNetworkTestedWithExtras."

parents 4624cdd1 a75e26be
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -676,7 +676,8 @@ public class ConnectivityDiagnosticsManager {
        }

        try {
            mService.registerConnectivityDiagnosticsCallback(binder, request);
            mService.registerConnectivityDiagnosticsCallback(
                    binder, request, mContext.getOpPackageName());
        } catch (RemoteException exception) {
            exception.rethrowFromSystemServer();
        }
+1 −1
Original line number Diff line number Diff line
@@ -222,7 +222,7 @@ interface IConnectivityManager
    boolean isCallerCurrentAlwaysOnVpnLockdownApp();

    void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback,
            in NetworkRequest request);
            in NetworkRequest request, String callingPackageName);
    void unregisterConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback);

    IBinder startOrGetTestNetworkService();
+2 −2
Original line number Diff line number Diff line
@@ -858,8 +858,8 @@ public final class NetworkCapabilities implements Parcelable {
     *
     * <p>In general, user-supplied networks (such as WiFi networks) do not have an administrator.
     *
     * <p>An app is granted owner privileges over Networks that it supplies. Owner privileges
     * implicitly include administrator privileges.
     * <p>An app is granted owner privileges over Networks that it supplies. The owner UID MUST
     * always be included in administratorUids.
     *
     * @param administratorUids the UIDs to be set as administrators of this Network.
     * @hide
+266 −88
Original line number Diff line number Diff line
@@ -48,8 +48,11 @@ import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;

import static java.util.Map.Entry;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -62,6 +65,7 @@ import android.content.res.Configuration;
import android.database.ContentObserver;
import android.net.CaptivePortal;
import android.net.ConnectionInfo;
import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import android.net.ConnectivityManager;
import android.net.ICaptivePortal;
import android.net.IConnectivityDiagnosticsCallback;
@@ -130,6 +134,7 @@ import android.os.Message;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -170,6 +175,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
@@ -492,9 +498,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
     /**
      * Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has
      * been tested.
      * obj = String representing URL that Internet probe was redirect to, if it was redirected.
      * arg1 = One of the NETWORK_TESTED_RESULT_* constants.
      * arg2 = NetID.
      * obj = {@link NetworkTestedResults} representing information sent from NetworkMonitor.
      * data = PersistableBundle of extras passed from NetworkMonitor. If {@link
      * NetworkMonitorCallbacks#notifyNetworkTested} is called, this will be null.
      */
    private static final int EVENT_NETWORK_TESTED = 41;

@@ -596,6 +602,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
    private Set<String> mWolSupportedInterfaces;

    private TelephonyManager mTelephonyManager;
    private final AppOpsManager mAppOpsManager;

    private final LocationPermissionChecker mLocationPermissionChecker;

    private KeepaliveTracker mKeepaliveTracker;
    private NetworkNotificationManager mNotifier;
@@ -992,6 +1001,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
        mNetd = netd;
        mKeyStore = KeyStore.getInstance();
        mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
        mLocationPermissionChecker = new LocationPermissionChecker(mContext);

        // To ensure uid rules are synchronized with Network Policy, register for
        // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
@@ -2101,6 +2112,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
    }

    private boolean checkNetworkStackPermission(int pid, int uid) {
        return checkAnyPermissionOf(pid, uid,
                android.Manifest.permission.NETWORK_STACK,
                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
    }

    private boolean checkNetworkSignalStrengthWakeupPermission(int pid, int uid) {
        return checkAnyPermissionOf(pid, uid,
                android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP,
@@ -2747,15 +2764,79 @@ public class ConnectivityService extends IConnectivityManager.Stub
                    break;
                }
                case EVENT_NETWORK_TESTED: {
                    final NetworkTestedResults results = (NetworkTestedResults) msg.obj;

                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(results.mNetId);
                    if (nai == null) break;

                    handleNetworkTested(nai, results.mTestResult,
                            (results.mRedirectUrl == null) ? "" : results.mRedirectUrl);

                    // Invoke ConnectivityReport generation for this Network test event.
                    final Message m =
                            mConnectivityDiagnosticsHandler.obtainMessage(
                                    ConnectivityDiagnosticsHandler.EVENT_NETWORK_TESTED,
                                    new ConnectivityReportEvent(results.mTimestampMillis, nai));
                    m.setData(msg.getData());
                    mConnectivityDiagnosticsHandler.sendMessage(m);
                    break;
                }
                case EVENT_PROVISIONING_NOTIFICATION: {
                    final int netId = msg.arg2;
                    final boolean visible = toBool(msg.arg1);
                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
                    // If captive portal status has changed, update capabilities or disconnect.
                    if (nai != null && (visible != nai.lastCaptivePortalDetected)) {
                        final int oldScore = nai.getCurrentScore();
                        nai.lastCaptivePortalDetected = visible;
                        nai.everCaptivePortalDetected |= visible;
                        if (nai.lastCaptivePortalDetected &&
                            Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
                            if (DBG) log("Avoiding captive portal network: " + nai.name());
                            nai.asyncChannel.sendMessage(
                                    NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
                            teardownUnneededNetwork(nai);
                            break;
                        }
                        updateCapabilities(oldScore, nai, nai.networkCapabilities);
                    }
                    if (!visible) {
                        // Only clear SIGN_IN and NETWORK_SWITCH notifications here, or else other
                        // notifications belong to the same network may be cleared unexpectedly.
                        mNotifier.clearNotification(netId, NotificationType.SIGN_IN);
                        mNotifier.clearNotification(netId, NotificationType.NETWORK_SWITCH);
                    } else {
                        if (nai == null) {
                            loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
                            break;
                        }
                        if (!nai.networkAgentConfig.provisioningNotificationDisabled) {
                            mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai, null,
                                    (PendingIntent) msg.obj,
                                    nai.networkAgentConfig.explicitlySelected);
                        }
                    }
                    break;
                }
                case EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                    if (nai == null) break;

                    updatePrivateDns(nai, (PrivateDnsConfig) msg.obj);
                    break;
                }
            }
            return true;
        }

        private void handleNetworkTested(
                @NonNull NetworkAgentInfo nai, int testResult, @NonNull String redirectUrl) {
            final boolean wasPartial = nai.partialConnectivity;
                    nai.partialConnectivity = ((msg.arg1 & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
            nai.partialConnectivity = ((testResult & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
            final boolean partialConnectivityChanged =
                    (wasPartial != nai.partialConnectivity);

                    final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0);
            final boolean valid = ((testResult & NETWORK_VALIDATION_RESULT_VALID) != 0);
            final boolean wasValidated = nai.lastValidated;
            final boolean wasDefault = isDefaultNetwork(nai);
            // Only show a connected notification if the network is pending validation
@@ -2766,8 +2847,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
                showNetworkNotification(nai, NotificationType.LOGGED_IN);
            }

                    final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";

            if (DBG) {
                final String logMsg = !TextUtils.isEmpty(redirectUrl)
                        ? " with redirect to " + redirectUrl
@@ -2829,54 +2908,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
            if (wasValidated && !nai.lastValidated) {
                handleNetworkUnvalidated(nai);
            }
                    break;
                }
                case EVENT_PROVISIONING_NOTIFICATION: {
                    final int netId = msg.arg2;
                    final boolean visible = toBool(msg.arg1);
                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
                    // If captive portal status has changed, update capabilities or disconnect.
                    if (nai != null && (visible != nai.lastCaptivePortalDetected)) {
                        final int oldScore = nai.getCurrentScore();
                        nai.lastCaptivePortalDetected = visible;
                        nai.everCaptivePortalDetected |= visible;
                        if (nai.lastCaptivePortalDetected &&
                            Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
                            if (DBG) log("Avoiding captive portal network: " + nai.name());
                            nai.asyncChannel.sendMessage(
                                    NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
                            teardownUnneededNetwork(nai);
                            break;
                        }
                        updateCapabilities(oldScore, nai, nai.networkCapabilities);
                    }
                    if (!visible) {
                        // Only clear SIGN_IN and NETWORK_SWITCH notifications here, or else other
                        // notifications belong to the same network may be cleared unexpectedly.
                        mNotifier.clearNotification(netId, NotificationType.SIGN_IN);
                        mNotifier.clearNotification(netId, NotificationType.NETWORK_SWITCH);
                    } else {
                        if (nai == null) {
                            loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
                            break;
                        }
                        if (!nai.networkAgentConfig.provisioningNotificationDisabled) {
                            mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai, null,
                                    (PendingIntent) msg.obj,
                                    nai.networkAgentConfig.explicitlySelected);
                        }
                    }
                    break;
                }
                case EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                    if (nai == null) break;

                    updatePrivateDns(nai, (PrivateDnsConfig) msg.obj);
                    break;
                }
            }
            return true;
        }

        private int getCaptivePortalMode() {
@@ -2927,8 +2958,23 @@ public class ConnectivityService extends IConnectivityManager.Stub

        @Override
        public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) {
            mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(EVENT_NETWORK_TESTED,
                    testResult, mNetId, redirectUrl));
            notifyNetworkTestedWithExtras(testResult, redirectUrl, SystemClock.elapsedRealtime(),
                    PersistableBundle.EMPTY);
        }

        @Override
        public void notifyNetworkTestedWithExtras(
                int testResult,
                @Nullable String redirectUrl,
                long timestampMillis,
                @NonNull PersistableBundle extras) {
            final Message msg =
                    mTrackerHandler.obtainMessage(
                            EVENT_NETWORK_TESTED,
                            new NetworkTestedResults(
                                    mNetId, testResult, timestampMillis, redirectUrl));
            msg.setData(new Bundle(extras));
            mTrackerHandler.sendMessage(msg);
        }

        @Override
@@ -7373,7 +7419,11 @@ public class ConnectivityService extends IConnectivityManager.Stub

    @GuardedBy("mVpns")
    private Vpn getVpnIfOwner() {
        final int uid = Binder.getCallingUid();
        return getVpnIfOwner(Binder.getCallingUid());
    }

    @GuardedBy("mVpns")
    private Vpn getVpnIfOwner(int uid) {
        final int user = UserHandle.getUserId(uid);

        final Vpn vpn = mVpns.get(user);
@@ -7485,6 +7535,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
         */
        private static final int EVENT_UNREGISTER_CONNECTIVITY_DIAGNOSTICS_CALLBACK = 2;

        /**
         * Event for {@link NetworkStateTrackerHandler} to trigger ConnectivityReport callbacks
         * after processing {@link #EVENT_NETWORK_TESTED} events.
         * obj = {@link ConnectivityReportEvent} representing ConnectivityReport info reported from
         * NetworkMonitor.
         * data = PersistableBundle of extras passed from NetworkMonitor.
         *
         * <p>See {@link ConnectivityService#EVENT_NETWORK_TESTED}.
         */
        private static final int EVENT_NETWORK_TESTED = ConnectivityService.EVENT_NETWORK_TESTED;

        private ConnectivityDiagnosticsHandler(Looper looper) {
            super(looper);
        }
@@ -7502,6 +7563,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
                            (IConnectivityDiagnosticsCallback) msg.obj, msg.arg1);
                    break;
                }
                case EVENT_NETWORK_TESTED: {
                    final ConnectivityReportEvent reportEvent =
                            (ConnectivityReportEvent) msg.obj;

                    // This is safe because {@link
                    // NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} receives a
                    // PersistableBundle and converts it to the Bundle in the incoming Message. If
                    // {@link NetworkMonitorCallbacks#notifyNetworkTested} is called, msg.data will
                    // not be set. This is also safe, as msg.getData() will return an empty Bundle.
                    final PersistableBundle extras = new PersistableBundle(msg.getData());
                    handleNetworkTestedWithExtras(reportEvent, extras);
                    break;
                }
            }
        }
    }
@@ -7511,12 +7585,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
    class ConnectivityDiagnosticsCallbackInfo implements Binder.DeathRecipient {
        @NonNull private final IConnectivityDiagnosticsCallback mCb;
        @NonNull private final NetworkRequestInfo mRequestInfo;
        @NonNull private final String mCallingPackageName;

        @VisibleForTesting
        ConnectivityDiagnosticsCallbackInfo(
                @NonNull IConnectivityDiagnosticsCallback cb, @NonNull NetworkRequestInfo nri) {
                @NonNull IConnectivityDiagnosticsCallback cb,
                @NonNull NetworkRequestInfo nri,
                @NonNull String callingPackageName) {
            mCb = cb;
            mRequestInfo = nri;
            mCallingPackageName = callingPackageName;
        }

        @Override
@@ -7526,6 +7604,39 @@ public class ConnectivityService extends IConnectivityManager.Stub
        }
    }

    /**
     * Class used for sending information from {@link
     * NetworkMonitorCallbacks#notifyNetworkTestedWithExtras} to the handler for processing it.
     */
    private static class NetworkTestedResults {
        private final int mNetId;
        private final int mTestResult;
        private final long mTimestampMillis;
        @Nullable private final String mRedirectUrl;

        private NetworkTestedResults(
                int netId, int testResult, long timestampMillis, @Nullable String redirectUrl) {
            mNetId = netId;
            mTestResult = testResult;
            mTimestampMillis = timestampMillis;
            mRedirectUrl = redirectUrl;
        }
    }

    /**
     * Class used for sending information from {@link NetworkStateTrackerHandler} to {@link
     * ConnectivityDiagnosticsHandler}.
     */
    private static class ConnectivityReportEvent {
        private final long mTimestampMillis;
        @NonNull private final NetworkAgentInfo mNai;

        private ConnectivityReportEvent(long timestampMillis, @NonNull NetworkAgentInfo nai) {
            mTimestampMillis = timestampMillis;
            mNai = nai;
        }
    }

    private void handleRegisterConnectivityDiagnosticsCallback(
            @NonNull ConnectivityDiagnosticsCallbackInfo cbInfo) {
        ensureRunningOnConnectivityServiceThread();
@@ -7573,13 +7684,80 @@ public class ConnectivityService extends IConnectivityManager.Stub
        cb.asBinder().unlinkToDeath(mConnectivityDiagnosticsCallbacks.remove(cb), 0);
    }

    private void handleNetworkTestedWithExtras(
            @NonNull ConnectivityReportEvent reportEvent, @NonNull PersistableBundle extras) {
        final NetworkAgentInfo nai = reportEvent.mNai;
        final ConnectivityReport report =
                new ConnectivityReport(
                        reportEvent.mNai.network,
                        reportEvent.mTimestampMillis,
                        nai.linkProperties,
                        nai.networkCapabilities,
                        extras);
        final List<IConnectivityDiagnosticsCallback> results =
                getMatchingPermissionedCallbacks(nai);
        for (final IConnectivityDiagnosticsCallback cb : results) {
            try {
                cb.onConnectivityReport(report);
            } catch (RemoteException ex) {
                loge("Error invoking onConnectivityReport", ex);
            }
        }
    }

    private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
            @NonNull NetworkAgentInfo nai) {
        final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
        for (Entry<IConnectivityDiagnosticsCallback, ConnectivityDiagnosticsCallbackInfo> entry :
                mConnectivityDiagnosticsCallbacks.entrySet()) {
            final ConnectivityDiagnosticsCallbackInfo cbInfo = entry.getValue();
            final NetworkRequestInfo nri = cbInfo.mRequestInfo;
            if (nai.satisfies(nri.request)) {
                if (checkConnectivityDiagnosticsPermissions(
                        nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
                    results.add(entry.getKey());
                }
            }
        }
        return results;
    }

    @VisibleForTesting
    boolean checkConnectivityDiagnosticsPermissions(
            int callbackPid, int callbackUid, NetworkAgentInfo nai, String callbackPackageName) {
        if (checkNetworkStackPermission(callbackPid, callbackUid)) {
            return true;
        }

        if (!mLocationPermissionChecker.checkLocationPermission(
                callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
            return false;
        }

        synchronized (mVpns) {
            if (getVpnIfOwner(callbackUid) != null) {
                return true;
            }
        }

        // Administrator UIDs also contains the Owner UID
        if (nai.networkCapabilities.getAdministratorUids().contains(callbackUid)) {
            return true;
        }

        return false;
    }

    @Override
    public void registerConnectivityDiagnosticsCallback(
            @NonNull IConnectivityDiagnosticsCallback callback, @NonNull NetworkRequest request) {
            @NonNull IConnectivityDiagnosticsCallback callback,
            @NonNull NetworkRequest request,
            @NonNull String callingPackageName) {
        if (request.legacyType != TYPE_NONE) {
            throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
                    + " Please use NetworkCapabilities instead.");
        }
        mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);

        // This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
        // and administrator uids to be safe.
@@ -7597,7 +7775,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
        // callback's binder death.
        final NetworkRequestInfo nri = new NetworkRequestInfo(requestWithId);
        final ConnectivityDiagnosticsCallbackInfo cbInfo =
                new ConnectivityDiagnosticsCallbackInfo(callback, nri);
                new ConnectivityDiagnosticsCallbackInfo(callback, nri, callingPackageName);

        mConnectivityDiagnosticsHandler.sendMessage(
                mConnectivityDiagnosticsHandler.obtainMessage(
+11 −4
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.Context;
import android.os.PersistableBundle;

import androidx.test.InstrumentationRegistry;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -58,21 +60,26 @@ public class ConnectivityDiagnosticsManagerTest {

    private static final Executor INLINE_EXECUTOR = x -> x.run();

    @Mock private Context mContext;
    @Mock private IConnectivityManager mService;
    @Mock private ConnectivityDiagnosticsCallback mCb;

    private Context mContext;
    private ConnectivityDiagnosticsBinder mBinder;
    private ConnectivityDiagnosticsManager mManager;

    private String mPackageName;

    @Before
    public void setUp() {
        mContext = mock(Context.class);
        mContext = InstrumentationRegistry.getContext();

        mService = mock(IConnectivityManager.class);
        mCb = mock(ConnectivityDiagnosticsCallback.class);

        mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR);
        mManager = new ConnectivityDiagnosticsManager(mContext, mService);

        mPackageName = mContext.getOpPackageName();
    }

    @After
@@ -271,7 +278,7 @@ public class ConnectivityDiagnosticsManagerTest {
        mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);

        verify(mService).registerConnectivityDiagnosticsCallback(
                any(ConnectivityDiagnosticsBinder.class), eq(request));
                any(ConnectivityDiagnosticsBinder.class), eq(request), eq(mPackageName));
        assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
    }

@@ -302,7 +309,7 @@ public class ConnectivityDiagnosticsManagerTest {
        // verify that re-registering is successful
        mManager.registerConnectivityDiagnosticsCallback(request, INLINE_EXECUTOR, mCb);
        verify(mService, times(2)).registerConnectivityDiagnosticsCallback(
                any(ConnectivityDiagnosticsBinder.class), eq(request));
                any(ConnectivityDiagnosticsBinder.class), eq(request), eq(mPackageName));
        assertTrue(ConnectivityDiagnosticsManager.sCallbacks.containsKey(mCb));
    }

Loading