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

Commit 45b9ed83 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Remove trusted networks in SystemUI."

parents c1f007a7 b50076d8
Loading
Loading
Loading
Loading
+0 −190
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.communal.conditions;

import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.wifi.WifiInfo;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;

import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.condition.Condition;
import com.android.systemui.util.settings.SecureSettings;

import java.util.Arrays;
import java.util.HashSet;

import javax.inject.Inject;

/**
 * Monitors Wi-Fi connections and triggers callback, if any, when the device is connected to and
 * disconnected from a trusted network.
 */
public class CommunalTrustedNetworkCondition extends Condition {
    private final String mTag = getClass().getSimpleName();
    private final ConnectivityManager mConnectivityManager;
    private final ContentObserver mTrustedNetworksObserver;
    private final SecureSettings mSecureSettings;

    // The SSID of the connected Wi-Fi network. Null if not connected to Wi-Fi.
    private String mWifiSSID;

    // Set of SSIDs of trusted networks.
    private final HashSet<String> mTrustedNetworks = new HashSet<>();

    /**
     * The deliminator used to separate trusted network keys saved as a string in secure settings.
     */
    public static final String SETTINGS_STRING_DELIMINATOR = ",/";

    @Inject
    public CommunalTrustedNetworkCondition(@Main Handler handler,
            ConnectivityManager connectivityManager, SecureSettings secureSettings) {
        mConnectivityManager = connectivityManager;
        mSecureSettings = secureSettings;

        mTrustedNetworksObserver = new ContentObserver(handler) {
            @Override
            public void onChange(boolean selfChange) {
                fetchTrustedNetworks();
            }
        };
    }

    /**
     * Starts monitoring for trusted network connection. Ignores if already started.
     */
    @Override
    protected void start() {
        if (shouldLog()) Log.d(mTag, "start listening for wifi connections");

        fetchTrustedNetworks();

        final NetworkRequest wifiNetworkRequest = new NetworkRequest.Builder().addTransportType(
                NetworkCapabilities.TRANSPORT_WIFI).build();
        mConnectivityManager.registerNetworkCallback(wifiNetworkRequest, mNetworkCallback);
        mSecureSettings.registerContentObserverForUser(
                Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS, false, mTrustedNetworksObserver,
                UserHandle.USER_SYSTEM);
    }

    /**
     * Stops monitoring for trusted network connection.
     */
    @Override
    protected void stop() {
        if (shouldLog()) Log.d(mTag, "stop listening for wifi connections");

        mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
        mSecureSettings.unregisterContentObserver(mTrustedNetworksObserver);
    }

    private void updateWifiInfo(WifiInfo wifiInfo) {
        if (wifiInfo == null) {
            mWifiSSID = null;
        } else {
            // Remove the wrapping quotes around the SSID.
            mWifiSSID = wifiInfo.getSSID().replace("\"", "");
        }

        checkIfConnectedToTrustedNetwork();
    }

    private void fetchTrustedNetworks() {
        final String trustedNetworksString = mSecureSettings.getStringForUser(
                Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS, UserHandle.USER_SYSTEM);
        mTrustedNetworks.clear();

        if (shouldLog()) Log.d(mTag, "fetched trusted networks: " + trustedNetworksString);

        if (TextUtils.isEmpty(trustedNetworksString)) {
            return;
        }

        mTrustedNetworks.addAll(
                Arrays.asList(trustedNetworksString.split(SETTINGS_STRING_DELIMINATOR)));

        checkIfConnectedToTrustedNetwork();
    }

    private void checkIfConnectedToTrustedNetwork() {
        final boolean connectedToTrustedNetwork = mWifiSSID != null && mTrustedNetworks.contains(
                mWifiSSID);

        if (shouldLog()) {
            Log.d(mTag, (connectedToTrustedNetwork ? "connected to" : "disconnected from")
                    + " a trusted network");
        }

        updateCondition(connectedToTrustedNetwork);
    }

    private final ConnectivityManager.NetworkCallback mNetworkCallback =
            new ConnectivityManager.NetworkCallback(
                    ConnectivityManager.NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
                private boolean mIsConnected = false;
                private WifiInfo mWifiInfo;

                @Override
                public void onAvailable(@NonNull Network network) {
                    super.onAvailable(network);

                    if (shouldLog()) Log.d(mTag, "connected to wifi");

                    mIsConnected = true;
                    if (mWifiInfo != null) {
                        updateWifiInfo(mWifiInfo);
                    }
                }

                @Override
                public void onLost(@NonNull Network network) {
                    super.onLost(network);

                    if (shouldLog()) Log.d(mTag, "disconnected from wifi");

                    mIsConnected = false;
                    mWifiInfo = null;
                    updateWifiInfo(null);
                }

                @Override
                public void onCapabilitiesChanged(@NonNull Network network,
                        @NonNull NetworkCapabilities networkCapabilities) {
                    super.onCapabilitiesChanged(network, networkCapabilities);

                    mWifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();

                    if (mIsConnected) {
                        updateWifiInfo(mWifiInfo);
                    }
                }
            };

    private boolean shouldLog() {
        return Log.isLoggable(mTag, Log.DEBUG);
    }
}
+0 −178
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.communal;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.wifi.WifiInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;

import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;
import com.android.systemui.communal.conditions.CommunalTrustedNetworkCondition;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.utils.os.FakeHandler;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

@SmallTest
@RunWith(AndroidTestingRunner.class)
public class CommunalTrustedNetworkConditionTest extends SysuiTestCase {
    @Mock private ConnectivityManager mConnectivityManager;

    @Captor private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor;

    private final Handler mHandler = new FakeHandler(Looper.getMainLooper());
    private CommunalTrustedNetworkCondition mCondition;

    private final String mTrustedWifi1 = "wifi-1";
    private final String mTrustedWifi2 = "wifi-2";

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        final FakeSettings secureSettings = new FakeSettings();
        secureSettings.putStringForUser(Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS,
                mTrustedWifi1 + CommunalTrustedNetworkCondition.SETTINGS_STRING_DELIMINATOR
                        + mTrustedWifi2, UserHandle.USER_SYSTEM);
        mCondition = new CommunalTrustedNetworkCondition(mHandler, mConnectivityManager,
                secureSettings);
    }

    @Test
    public void updateCallback_connectedToTrustedNetwork_reportsTrue() {
        final CommunalTrustedNetworkCondition.Callback callback =
                mock(CommunalTrustedNetworkCondition.Callback.class);
        mCondition.addCallback(callback);

        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();

        // Connected to trusted Wi-Fi network.
        final Network network = mock(Network.class);
        networkCallback.onAvailable(network);
        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));

        // Verifies that the callback is triggered.
        verify(callback).onConditionChanged(mCondition);
        assertThat(mCondition.isConditionMet()).isTrue();
    }

    @Test
    public void updateCallback_switchedToAnotherTrustedNetwork_reportsNothing() {
        final CommunalTrustedNetworkCondition.Callback callback =
                mock(CommunalTrustedNetworkCondition.Callback.class);
        mCondition.addCallback(callback);

        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();

        // Connected to a trusted Wi-Fi network.
        final Network network = mock(Network.class);
        networkCallback.onAvailable(network);
        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
        clearInvocations(callback);

        // Connected to another trusted Wi-Fi network.
        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi2));

        // Verifies that the callback is not triggered.
        verify(callback, never()).onConditionChanged(eq(mCondition));
    }

    @Test
    public void updateCallback_connectedToNonTrustedNetwork_reportsFalse() {
        final CommunalTrustedNetworkCondition.Callback callback =
                mock(CommunalTrustedNetworkCondition.Callback.class);
        mCondition.addCallback(callback);

        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();

        // Connected to trusted Wi-Fi network.
        final Network network = mock(Network.class);
        networkCallback.onAvailable(network);
        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));

        Mockito.clearInvocations(callback);
        // Connected to non-trusted Wi-Fi network.
        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities("random-wifi"));

        // Verifies that the callback is triggered.
        verify(callback).onConditionChanged(mCondition);
        assertThat(mCondition.isConditionMet()).isFalse();
    }

    @Test
    public void updateCallback_disconnectedFromNetwork_reportsFalse() {
        final CommunalTrustedNetworkCondition.Callback callback =
                mock(CommunalTrustedNetworkCondition.Callback.class);
        mCondition.addCallback(callback);

        final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();

        // Connected to Wi-Fi.
        final Network network = mock(Network.class);
        networkCallback.onAvailable(network);
        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
        clearInvocations(callback);

        // Disconnected from Wi-Fi.
        networkCallback.onLost(network);

        // Verifies that the callback is triggered.
        verify(callback).onConditionChanged(mCondition);
        assertThat(mCondition.isConditionMet()).isFalse();
    }

    // Captures and returns the network callback, assuming it is registered with the connectivity
    // manager.
    private ConnectivityManager.NetworkCallback captureNetworkCallback() {
        verify(mConnectivityManager).registerNetworkCallback(any(NetworkRequest.class),
                mNetworkCallbackCaptor.capture());
        return mNetworkCallbackCaptor.getValue();
    }

    private NetworkCapabilities fakeNetworkCapabilities(String ssid) {
        final NetworkCapabilities networkCapabilities = mock(NetworkCapabilities.class);
        final WifiInfo wifiInfo = mock(WifiInfo.class);
        when(wifiInfo.getSSID()).thenReturn(ssid);
        when(networkCapabilities.getTransportInfo()).thenReturn(wifiInfo);
        return networkCapabilities;
    }
}