Merge changes Ib975d5a7,Idcc93f9e,Ibc0f30d4,I5b09ec1d

* changes:
  Check whether a Wi-Fi network is trusted.
  Introduce communal trusted network condition.
  Introduce communal setting condition.
  Introduction to communal conditions monitor.
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
index 2244532..d3018e3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
@@ -16,17 +16,11 @@
 
 package com.android.systemui.communal;
 
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.Log;
 
-import androidx.annotation.MainThread;
-
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.util.settings.SecureSettings;
 
 import com.google.android.collect.Lists;
 
@@ -46,10 +40,15 @@
 
     // A list of {@link Callback} that have registered to receive updates.
     private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
-    private final SecureSettings mSecureSettings;
+    private final CommunalConditionsMonitor mConditionsMonitor;
 
     private CommunalSource mCurrentSource;
-    private boolean mCommunalEnabled;
+
+    // Whether all conditions for communal mode to show have been met.
+    private boolean mAllCommunalConditionsMet = false;
+
+    // Whether the class is currently listening for condition changes.
+    private boolean mListeningForConditions = false;
 
     private CommunalSource.Callback mSourceCallback = new CommunalSource.Callback() {
         @Override
@@ -59,24 +58,20 @@
         }
     };
 
+    private final CommunalConditionsMonitor.Callback mConditionsCallback =
+            allConditionsMet -> {
+                if (mAllCommunalConditionsMet != allConditionsMet) {
+                    if (DEBUG) Log.d(TAG, "communal conditions changed: " + allConditionsMet);
+
+                    mAllCommunalConditionsMet = allConditionsMet;
+                    executeOnSourceAvailableCallbacks();
+                }
+            };
+
     @VisibleForTesting
     @Inject
-    public CommunalSourceMonitor(
-            @MainThread Handler mainThreadHandler,
-            SecureSettings secureSettings) {
-        mSecureSettings = secureSettings;
-
-        ContentObserver settingsObserver = new ContentObserver(mainThreadHandler) {
-            @Override
-            public void onChange(boolean selfChange) {
-                reloadSettings();
-            }
-        };
-        mSecureSettings.registerContentObserverForUser(
-                Settings.Secure.COMMUNAL_MODE_ENABLED,
-                /* notifyForDescendants= */false,
-                settingsObserver, UserHandle.USER_SYSTEM);
-        reloadSettings();
+    public CommunalSourceMonitor(CommunalConditionsMonitor communalConditionsMonitor) {
+        mConditionsMonitor = communalConditionsMonitor;
     }
 
     /**
@@ -92,7 +87,7 @@
 
         mCurrentSource = source;
 
-        if (mCommunalEnabled) {
+        if (mAllCommunalConditionsMet) {
             executeOnSourceAvailableCallbacks();
         }
 
@@ -111,7 +106,7 @@
                 itr.remove();
             } else {
                 cb.onSourceAvailable(
-                        (mCommunalEnabled && mCurrentSource != null) ? new WeakReference<>(
+                        (mAllCommunalConditionsMet && mCurrentSource != null) ? new WeakReference<>(
                                 mCurrentSource) : null);
             }
         }
@@ -126,9 +121,14 @@
         mCallbacks.add(new WeakReference<>(callback));
 
         // Inform the callback of any already present CommunalSource.
-        if (mCommunalEnabled && mCurrentSource != null) {
+        if (mAllCommunalConditionsMet && mCurrentSource != null) {
             callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
         }
+
+        if (!mListeningForConditions) {
+            mConditionsMonitor.addCallback(mConditionsCallback);
+            mListeningForConditions = true;
+        }
     }
 
     /**
@@ -138,21 +138,10 @@
      */
     public void removeCallback(Callback callback) {
         mCallbacks.removeIf(el -> el.get() == callback);
-    }
 
-    private void reloadSettings() {
-        boolean newCommunalEnabled = mSecureSettings.getIntForUser(
-                Settings.Secure.COMMUNAL_MODE_ENABLED,
-                1,
-                UserHandle.USER_SYSTEM) == 1;
-
-        if (DEBUG) {
-            Log.d(TAG, "communal mode settings reloaded with value:" + newCommunalEnabled);
-        }
-
-        if (mCommunalEnabled != newCommunalEnabled) {
-            mCommunalEnabled = newCommunalEnabled;
-            executeOnSourceAvailableCallbacks();
+        if (mCallbacks.isEmpty() && mListeningForConditions) {
+            mConditionsMonitor.removeCallback(mConditionsCallback);
+            mListeningForConditions = false;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalCondition.java
new file mode 100644
index 0000000..734ab63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalCondition.java
@@ -0,0 +1,130 @@
+/*
+ * 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.util.Log;
+
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Base class for a condition that needs to be fulfilled in order for Communal Mode to display.
+ */
+public abstract class CommunalCondition implements CallbackController<CommunalCondition.Callback> {
+    private final String mTag = getClass().getSimpleName();
+
+    private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+    private boolean mIsConditionMet = false;
+    private boolean mStarted = false;
+
+    /**
+     * Starts monitoring the condition.
+     */
+    protected abstract void start();
+
+    /**
+     * Stops monitoring the condition.
+     */
+    protected abstract void stop();
+
+    /**
+     * Registers a callback to receive updates once started. This should be called before
+     * {@link #start()}. Also triggers the callback immediately if already started.
+     */
+    @Override
+    public void addCallback(@NotNull Callback callback) {
+        if (shouldLog()) Log.d(mTag, "adding callback");
+        mCallbacks.add(new WeakReference<>(callback));
+
+        if (mStarted) {
+            callback.onConditionChanged(this, mIsConditionMet);
+            return;
+        }
+
+        start();
+        mStarted = true;
+    }
+
+    /**
+     * Removes the provided callback from further receiving updates.
+     */
+    @Override
+    public void removeCallback(@NotNull Callback callback) {
+        if (shouldLog()) Log.d(mTag, "removing callback");
+        final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+        while (iterator.hasNext()) {
+            final Callback cb = iterator.next().get();
+            if (cb == null || cb == callback) {
+                iterator.remove();
+            }
+        }
+
+        if (!mCallbacks.isEmpty() || !mStarted) {
+            return;
+        }
+
+        stop();
+        mStarted = false;
+    }
+
+    /**
+     * Updates the value for whether the condition has been fulfilled, and sends an update if the
+     * value changes and any callback is registered.
+     *
+     * @param isConditionMet True if the condition has been fulfilled. False otherwise.
+     */
+    protected void updateCondition(boolean isConditionMet) {
+        if (mIsConditionMet == isConditionMet) {
+            return;
+        }
+
+        if (shouldLog()) Log.d(mTag, "updating condition to " + isConditionMet);
+        mIsConditionMet = isConditionMet;
+
+        final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+        while (iterator.hasNext()) {
+            final Callback cb = iterator.next().get();
+            if (cb == null) {
+                iterator.remove();
+            } else {
+                cb.onConditionChanged(this, mIsConditionMet);
+            }
+        }
+    }
+
+    private boolean shouldLog() {
+        return Log.isLoggable(mTag, Log.DEBUG);
+    }
+
+    /**
+     * Callback that receives updates about whether the condition has been fulfilled.
+     */
+    public interface Callback {
+        /**
+         * Called when the fulfillment of the condition changes.
+         *
+         * @param condition The condition in question.
+         * @param isConditionMet True if the condition has been fulfilled. False otherwise.
+         */
+        void onConditionChanged(CommunalCondition condition, boolean isConditionMet);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java
new file mode 100644
index 0000000..4f772da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java
@@ -0,0 +1,154 @@
+/*
+ * 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 static com.android.systemui.communal.dagger.CommunalModule.COMMUNAL_CONDITIONS;
+
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link CommunalConditionsMonitor} takes in a set of conditions, monitors whether all of them have
+ * been fulfilled, and informs any registered listeners.
+ */
+@SysUISingleton
+public class CommunalConditionsMonitor implements
+        CallbackController<CommunalConditionsMonitor.Callback> {
+    private final String mTag = getClass().getSimpleName();
+
+    private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+
+    // Set of all conditions that need to be monitored.
+    private final Set<CommunalCondition> mConditions;
+
+    // Map of values of each condition.
+    private final HashMap<CommunalCondition, Boolean> mConditionsMap = new HashMap<>();
+
+    // Whether all conditions have been met.
+    private boolean mAllConditionsMet = false;
+
+    // Whether the monitor has started listening for all the conditions.
+    private boolean mHaveConditionsStarted = false;
+
+    // Callback for when each condition has been updated.
+    private final CommunalCondition.Callback mConditionCallback = (condition, isConditionMet) -> {
+        mConditionsMap.put(condition, isConditionMet);
+
+        final boolean newAllConditionsMet = !mConditionsMap.containsValue(false);
+
+        if (newAllConditionsMet == mAllConditionsMet) {
+            return;
+        }
+
+        if (shouldLog()) Log.d(mTag, "all conditions met: " + newAllConditionsMet);
+        mAllConditionsMet = newAllConditionsMet;
+
+        // Updates all callbacks.
+        final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+        while (iterator.hasNext()) {
+            final Callback callback = iterator.next().get();
+            if (callback == null) {
+                iterator.remove();
+            } else {
+                callback.onConditionsChanged(mAllConditionsMet);
+            }
+        }
+    };
+
+    @Inject
+    public CommunalConditionsMonitor(
+            @Named(COMMUNAL_CONDITIONS) Set<CommunalCondition> communalConditions) {
+        mConditions = communalConditions;
+
+        // Initializes the conditions map and registers a callback for each condition.
+        mConditions.forEach((condition -> mConditionsMap.put(condition, false)));
+    }
+
+    @Override
+    public void addCallback(@NotNull Callback callback) {
+        if (shouldLog()) Log.d(mTag, "adding callback");
+        mCallbacks.add(new WeakReference<>(callback));
+
+        // Updates the callback immediately.
+        callback.onConditionsChanged(mAllConditionsMet);
+
+        if (!mHaveConditionsStarted) {
+            if (shouldLog()) Log.d(mTag, "starting all conditions");
+            mConditions.forEach(condition -> condition.addCallback(mConditionCallback));
+            mHaveConditionsStarted = true;
+        }
+    }
+
+    @Override
+    public void removeCallback(@NotNull Callback callback) {
+        if (shouldLog()) Log.d(mTag, "removing callback");
+        final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+        while (iterator.hasNext()) {
+            final Callback cb = iterator.next().get();
+            if (cb == null || cb == callback) {
+                iterator.remove();
+            }
+        }
+
+        if (mCallbacks.isEmpty() && mHaveConditionsStarted) {
+            if (shouldLog()) Log.d(mTag, "stopping all conditions");
+            mConditions.forEach(condition -> condition.removeCallback(mConditionCallback));
+
+            mAllConditionsMet = false;
+            mHaveConditionsStarted = false;
+        }
+    }
+
+    /**
+     * Force updates each condition to the value provided.
+     */
+    @VisibleForTesting
+    public void overrideAllConditionsMet(boolean value) {
+        mConditions.forEach(condition -> condition.updateCondition(value));
+    }
+
+    private boolean shouldLog() {
+        return Log.isLoggable(mTag, Log.DEBUG);
+    }
+
+    /**
+     * Callback that receives updates of whether all conditions have been fulfilled.
+     */
+    public interface Callback {
+        /**
+         * Triggered when the fulfillment of all conditions have been met.
+         *
+         * @param allConditionsMet True if all conditions have been fulfilled. False if none or
+         *                         only partial conditions have been fulfilled.
+         */
+        void onConditionsChanged(boolean allConditionsMet);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java
new file mode 100644
index 0000000..1616b18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java
@@ -0,0 +1,66 @@
+/*
+ * 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.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.annotation.MainThread;
+
+import com.android.systemui.util.settings.SecureSettings;
+
+import javax.inject.Inject;
+
+/**
+ * Monitors the communal setting, and informs any listeners with updates.
+ */
+public class CommunalSettingCondition extends CommunalCondition {
+    private final SecureSettings mSecureSettings;
+    private final ContentObserver mCommunalSettingContentObserver;
+
+    @Inject
+    public CommunalSettingCondition(@MainThread Handler mainHandler,
+            SecureSettings secureSettings) {
+        mSecureSettings = secureSettings;
+
+        mCommunalSettingContentObserver = new ContentObserver(mainHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                final boolean communalSettingEnabled = mSecureSettings.getIntForUser(
+                        Settings.Secure.COMMUNAL_MODE_ENABLED, 0, UserHandle.USER_SYSTEM) == 1;
+                updateCondition(communalSettingEnabled);
+            }
+        };
+    }
+
+    @Override
+    protected void start() {
+        mSecureSettings.registerContentObserverForUser(Settings.Secure.COMMUNAL_MODE_ENABLED,
+                false /*notifyForDescendants*/, mCommunalSettingContentObserver,
+                UserHandle.USER_SYSTEM);
+
+        // Fetches setting immediately.
+        mCommunalSettingContentObserver.onChange(false);
+    }
+
+    @Override
+    protected void stop() {
+        mSecureSettings.unregisterContentObserver(mCommunalSettingContentObserver);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java
new file mode 100644
index 0000000..e4692db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java
@@ -0,0 +1,189 @@
+/*
+ * 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.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 CommunalCondition {
+    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);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
index 31dd82d..3ebfb51 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -20,15 +20,23 @@
 import android.view.View;
 import android.widget.FrameLayout;
 
+import com.android.systemui.communal.conditions.CommunalCondition;
+import com.android.systemui.communal.conditions.CommunalSettingCondition;
+import com.android.systemui.communal.conditions.CommunalTrustedNetworkCondition;
 import com.android.systemui.idle.AmbientLightModeMonitor;
 import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
 import com.android.systemui.idle.dagger.IdleViewComponent;
 
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
 import javax.inject.Named;
 
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.ElementsIntoSet;
 
 /**
  * Dagger Module providing Communal-related functionality.
@@ -39,6 +47,7 @@
 })
 public interface CommunalModule {
     String IDLE_VIEW = "idle_view";
+    String COMMUNAL_CONDITIONS = "communal_conditions";
 
     /** */
     @Provides
@@ -56,4 +65,17 @@
     @Binds
     AmbientLightModeMonitor.DebounceAlgorithm ambientLightDebounceAlgorithm(
             LightSensorEventsDebounceAlgorithm algorithm);
+
+    /**
+     * Provides a set of conditions that need to be fulfilled in order for Communal Mode to display.
+     */
+    @Provides
+    @ElementsIntoSet
+    @Named(COMMUNAL_CONDITIONS)
+    static Set<CommunalCondition> provideCommunalConditions(
+            CommunalSettingCondition communalSettingCondition,
+            CommunalTrustedNetworkCondition communalTrustedNetworkCondition) {
+        return new HashSet<>(
+                Arrays.asList(communalSettingCondition, communalTrustedNetworkCondition));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionTest.java
new file mode 100644
index 0000000..9d7ef0f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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 org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.conditions.CommunalCondition;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalConditionTest extends SysuiTestCase {
+    private FakeCommunalCondition mCondition;
+
+    @Before
+    public void setup() {
+        mCondition = spy(new FakeCommunalCondition());
+    }
+
+    @Test
+    public void addCallback_addFirstCallback_triggerStart() {
+        final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback);
+        verify(mCondition).start();
+    }
+
+    @Test
+    public void addCallback_addMultipleCallbacks_triggerStartOnlyOnce() {
+        final CommunalCondition.Callback callback1 = mock(CommunalCondition.Callback.class);
+        final CommunalCondition.Callback callback2 = mock(CommunalCondition.Callback.class);
+        final CommunalCondition.Callback callback3 = mock(CommunalCondition.Callback.class);
+
+        mCondition.addCallback(callback1);
+        mCondition.addCallback(callback2);
+        mCondition.addCallback(callback3);
+
+        verify(mCondition, times(1)).start();
+    }
+
+    @Test
+    public void addCallback_alreadyStarted_triggerUpdate() {
+        final CommunalCondition.Callback callback1 = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback1);
+
+        mCondition.fakeUpdateCondition(true);
+
+        final CommunalCondition.Callback callback2 = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback2);
+        verify(callback2).onConditionChanged(mCondition, true);
+    }
+
+    @Test
+    public void removeCallback_removeLastCallback_triggerStop() {
+        final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback);
+        verify(mCondition, never()).stop();
+
+        mCondition.removeCallback(callback);
+        verify(mCondition).stop();
+    }
+
+    @Test
+    public void updateCondition_falseToTrue_reportTrue() {
+        mCondition.fakeUpdateCondition(false);
+
+        final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback);
+
+        mCondition.fakeUpdateCondition(true);
+        verify(callback).onConditionChanged(eq(mCondition), eq(true));
+    }
+
+    @Test
+    public void updateCondition_trueToFalse_reportFalse() {
+        mCondition.fakeUpdateCondition(true);
+
+        final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback);
+
+        mCondition.fakeUpdateCondition(false);
+        verify(callback).onConditionChanged(eq(mCondition), eq(false));
+    }
+
+    @Test
+    public void updateCondition_trueToTrue_reportNothing() {
+        mCondition.fakeUpdateCondition(true);
+
+        final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback);
+
+        mCondition.fakeUpdateCondition(true);
+        verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+    }
+
+    @Test
+    public void updateCondition_falseToFalse_reportNothing() {
+        mCondition.fakeUpdateCondition(false);
+
+        final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback);
+
+        mCondition.fakeUpdateCondition(false);
+        verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionsMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionsMonitorTest.java
new file mode 100644
index 0000000..59ddba1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionsMonitorTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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 org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.conditions.CommunalCondition;
+import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalConditionsMonitorTest extends SysuiTestCase {
+    private FakeCommunalCondition mCondition1;
+    private FakeCommunalCondition mCondition2;
+    private FakeCommunalCondition mCondition3;
+    private HashSet<CommunalCondition> mConditions;
+
+    private CommunalConditionsMonitor mCommunalConditionsMonitor;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        mCondition1 = spy(new FakeCommunalCondition());
+        mCondition2 = spy(new FakeCommunalCondition());
+        mCondition3 = spy(new FakeCommunalCondition());
+        mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3));
+
+        mCommunalConditionsMonitor = new CommunalConditionsMonitor(mConditions);
+    }
+
+    @Test
+    public void addCallback_addFirstCallback_addCallbackToAllConditions() {
+        final CommunalConditionsMonitor.Callback callback1 =
+                mock(CommunalConditionsMonitor.Callback.class);
+        mCommunalConditionsMonitor.addCallback(callback1);
+        mConditions.forEach(condition -> verify(condition).addCallback(any()));
+
+        final CommunalConditionsMonitor.Callback callback2 =
+                mock(CommunalConditionsMonitor.Callback.class);
+        mCommunalConditionsMonitor.addCallback(callback2);
+        mConditions.forEach(condition -> verify(condition, times(1)).addCallback(any()));
+    }
+
+    @Test
+    public void addCallback_addFirstCallback_reportWithDefaultValue() {
+        final CommunalConditionsMonitor.Callback callback =
+                mock(CommunalConditionsMonitor.Callback.class);
+        mCommunalConditionsMonitor.addCallback(callback);
+        verify(callback).onConditionsChanged(false);
+    }
+
+    @Test
+    public void addCallback_addSecondCallback_reportWithExistingValue() {
+        final CommunalConditionsMonitor.Callback callback1 =
+                mock(CommunalConditionsMonitor.Callback.class);
+        mCommunalConditionsMonitor.addCallback(callback1);
+
+        mCommunalConditionsMonitor.overrideAllConditionsMet(true);
+
+        final CommunalConditionsMonitor.Callback callback2 =
+                mock(CommunalConditionsMonitor.Callback.class);
+        mCommunalConditionsMonitor.addCallback(callback2);
+        verify(callback2).onConditionsChanged(true);
+    }
+
+    @Test
+    public void removeCallback_shouldNoLongerReceiveUpdate() {
+        final CommunalConditionsMonitor.Callback callback =
+                mock(CommunalConditionsMonitor.Callback.class);
+        mCommunalConditionsMonitor.addCallback(callback);
+        clearInvocations(callback);
+        mCommunalConditionsMonitor.removeCallback(callback);
+
+        mCommunalConditionsMonitor.overrideAllConditionsMet(true);
+        verify(callback, never()).onConditionsChanged(true);
+
+        mCommunalConditionsMonitor.overrideAllConditionsMet(false);
+        verify(callback, never()).onConditionsChanged(false);
+    }
+
+    @Test
+    public void removeCallback_removeLastCallback_removeCallbackFromAllConditions() {
+        final CommunalConditionsMonitor.Callback callback1 =
+                mock(CommunalConditionsMonitor.Callback.class);
+        final CommunalConditionsMonitor.Callback callback2 =
+                mock(CommunalConditionsMonitor.Callback.class);
+        mCommunalConditionsMonitor.addCallback(callback1);
+        mCommunalConditionsMonitor.addCallback(callback2);
+
+        mCommunalConditionsMonitor.removeCallback(callback1);
+        mConditions.forEach(condition -> verify(condition, never()).removeCallback(any()));
+
+        mCommunalConditionsMonitor.removeCallback(callback2);
+        mConditions.forEach(condition -> verify(condition).removeCallback(any()));
+    }
+
+    @Test
+    public void updateCallbacks_allConditionsMet_reportTrue() {
+        final CommunalConditionsMonitor.Callback callback =
+                mock(CommunalConditionsMonitor.Callback.class);
+        mCommunalConditionsMonitor.addCallback(callback);
+        clearInvocations(callback);
+
+        mCondition1.fakeUpdateCondition(true);
+        mCondition2.fakeUpdateCondition(true);
+        mCondition3.fakeUpdateCondition(true);
+
+        verify(callback).onConditionsChanged(true);
+    }
+
+    @Test
+    public void updateCallbacks_oneConditionStoppedMeeting_reportFalse() {
+        final CommunalConditionsMonitor.Callback callback =
+                mock(CommunalConditionsMonitor.Callback.class);
+        mCommunalConditionsMonitor.addCallback(callback);
+
+        mCondition1.fakeUpdateCondition(true);
+        mCondition2.fakeUpdateCondition(true);
+        mCondition3.fakeUpdateCondition(true);
+        clearInvocations(callback);
+
+        mCondition1.fakeUpdateCondition(false);
+        verify(callback).onConditionsChanged(false);
+    }
+
+    @Test
+    public void updateCallbacks_shouldOnlyUpdateWhenValueChanges() {
+        final CommunalConditionsMonitor.Callback callback =
+                mock(CommunalConditionsMonitor.Callback.class);
+        mCommunalConditionsMonitor.addCallback(callback);
+        verify(callback).onConditionsChanged(false);
+        clearInvocations(callback);
+
+        mCondition1.fakeUpdateCondition(true);
+        verify(callback, never()).onConditionsChanged(anyBoolean());
+
+        mCondition2.fakeUpdateCondition(true);
+        verify(callback, never()).onConditionsChanged(anyBoolean());
+
+        mCondition3.fakeUpdateCondition(true);
+        verify(callback).onConditionsChanged(true);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
new file mode 100644
index 0000000..cf147f0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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 org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+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 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.CommunalCondition;
+import com.android.systemui.communal.conditions.CommunalSettingCondition;
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalSettingConditionTest extends SysuiTestCase {
+    private FakeSettings mSecureSettings;
+    private CommunalSettingCondition mCondition;
+
+    @Before
+    public void setup() {
+        final FakeHandler handler = new FakeHandler(Looper.getMainLooper());
+        mSecureSettings = new FakeSettings();
+        mCondition = new CommunalSettingCondition(handler, mSecureSettings);
+    }
+
+    @Test
+    public void addCallback_communalSettingEnabled_immediatelyReportsTrue() {
+        updateCommunalSetting(true);
+
+        final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback);
+        verify(callback).onConditionChanged(mCondition, true);
+    }
+
+    @Test
+    public void addCallback_communalSettingDisabled_noReport() {
+        updateCommunalSetting(false);
+
+        final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback);
+        verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+    }
+
+    @Test
+    public void updateCallback_communalSettingEnabled_reportsTrue() {
+        updateCommunalSetting(false);
+
+        final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback);
+        clearInvocations(callback);
+
+        updateCommunalSetting(true);
+        verify(callback).onConditionChanged(mCondition, true);
+    }
+
+    @Test
+    public void updateCallback_communalSettingDisabled_reportsFalse() {
+        updateCommunalSetting(true);
+
+        final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback);
+        clearInvocations(callback);
+
+        updateCommunalSetting(false);
+        verify(callback).onConditionChanged(mCondition, false);
+    }
+
+    @Test
+    public void updateCallback_communalSettingDidNotChange_neverReportDup() {
+        updateCommunalSetting(true);
+
+        final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+        mCondition.addCallback(callback);
+        clearInvocations(callback);
+
+        updateCommunalSetting(true);
+        verify(callback, never()).onConditionChanged(mCondition, true);
+    }
+
+    private void updateCommunalSetting(boolean value) {
+        mSecureSettings.putIntForUser(Settings.Secure.COMMUNAL_MODE_ENABLED, value ? 1 : 0,
+                UserHandle.USER_SYSTEM);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
index 68600de..df9a2cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
@@ -20,25 +20,25 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.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 android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
 
 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.MockitoAnnotations;
 
 import java.lang.ref.WeakReference;
@@ -47,15 +47,16 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class CommunalSourceMonitorTest extends SysuiTestCase {
+    @Mock private CommunalConditionsMonitor mCommunalConditionsMonitor;
+
+    @Captor private ArgumentCaptor<CommunalConditionsMonitor.Callback> mConditionsCallbackCaptor;
+
     private CommunalSourceMonitor mCommunalSourceMonitor;
-    private FakeSettings mSecureSettings;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mSecureSettings = new FakeSettings();
-        mCommunalSourceMonitor = new CommunalSourceMonitor(
-                Handler.createAsync(TestableLooper.get(this).getLooper()), mSecureSettings);
+        mCommunalSourceMonitor = new CommunalSourceMonitor(mCommunalConditionsMonitor);
     }
 
     @Test
@@ -65,6 +66,7 @@
 
         mCommunalSourceMonitor.setSource(source);
         mCommunalSourceMonitor.addCallback(callback);
+        setConditionsMet(true);
 
         verifyOnSourceAvailableCalledWith(callback, source);
     }
@@ -82,33 +84,32 @@
     }
 
     @Test
-    public void testAddCallbackWithDefaultSetting() {
+    public void testAddCallbackWithConditionsMet() {
         final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
         final CommunalSource source = mock(CommunalSource.class);
 
         mCommunalSourceMonitor.addCallback(callback);
+        setConditionsMet(true);
+        clearInvocations(callback);
         mCommunalSourceMonitor.setSource(source);
 
         verifyOnSourceAvailableCalledWith(callback, source);
     }
 
     @Test
-    public void testAddCallbackWithSettingDisabled() {
-        setCommunalEnabled(false);
-
+    public void testAddCallbackWithConditionsNotMet() {
         final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
         final CommunalSource source = mock(CommunalSource.class);
 
         mCommunalSourceMonitor.addCallback(callback);
+        setConditionsMet(false);
         mCommunalSourceMonitor.setSource(source);
 
         verify(callback, never()).onSourceAvailable(any());
     }
 
     @Test
-    public void testSettingEnabledAfterCallbackAdded() {
-        setCommunalEnabled(false);
-
+    public void testConditionsAreMetAfterCallbackAdded() {
         final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
         final CommunalSource source = mock(CommunalSource.class);
 
@@ -118,33 +119,27 @@
         // The callback should not have executed since communal is disabled.
         verify(callback, never()).onSourceAvailable(any());
 
-        // The callback should execute when the user changes the setting to enabled.
-        setCommunalEnabled(true);
+        // The callback should execute when all conditions are met.
+        setConditionsMet(true);
         verifyOnSourceAvailableCalledWith(callback, source);
     }
 
     @Test
-    public void testSettingDisabledAfterCallbackAdded() {
+    public void testConditionsNoLongerMetAfterCallbackAdded() {
         final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
         final CommunalSource source = mock(CommunalSource.class);
 
         mCommunalSourceMonitor.addCallback(callback);
         mCommunalSourceMonitor.setSource(source);
+        setConditionsMet(true);
         verifyOnSourceAvailableCalledWith(callback, source);
 
-        // The callback should execute again when the setting is disabled, with a value of null.
-        setCommunalEnabled(false);
+        // The callback should execute again when conditions are no longer met, with a value of
+        // null.
+        setConditionsMet(false);
         verify(callback).onSourceAvailable(null);
     }
 
-    private void setCommunalEnabled(boolean enabled) {
-        mSecureSettings.putIntForUser(
-                Settings.Secure.COMMUNAL_MODE_ENABLED,
-                enabled ? 1 : 0,
-                UserHandle.USER_SYSTEM);
-        TestableLooper.get(this).processAllMessages();
-    }
-
     private void verifyOnSourceAvailableCalledWith(CommunalSourceMonitor.Callback callback,
             CommunalSource source) {
         final ArgumentCaptor<WeakReference<CommunalSource>> sourceCapture =
@@ -152,4 +147,13 @@
         verify(callback).onSourceAvailable(sourceCapture.capture());
         assertThat(sourceCapture.getValue().get()).isEqualTo(source);
     }
+
+    // Pushes an update on whether the communal conditions are met, assuming that a callback has
+    // been registered with the communal conditions monitor.
+    private void setConditionsMet(boolean value) {
+        verify(mCommunalConditionsMonitor).addCallback(mConditionsCallbackCaptor.capture());
+        final CommunalConditionsMonitor.Callback conditionsCallback =
+                mConditionsCallbackCaptor.getValue();
+        conditionsCallback.onConditionsChanged(value);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
new file mode 100644
index 0000000..61a5126
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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 org.mockito.ArgumentMatchers.anyBoolean;
+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.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, true);
+    }
+
+    @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), anyBoolean());
+    }
+
+    @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));
+
+        // Connected to non-trusted Wi-Fi network.
+        networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities("random-wifi"));
+
+        // Verifies that the callback is triggered.
+        verify(callback).onConditionChanged(mCondition, false);
+    }
+
+    @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, false);
+    }
+
+    // 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;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/FakeCommunalCondition.java b/packages/SystemUI/tests/src/com/android/systemui/communal/FakeCommunalCondition.java
new file mode 100644
index 0000000..882effd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/FakeCommunalCondition.java
@@ -0,0 +1,35 @@
+/*
+ * 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 com.android.systemui.communal.conditions.CommunalCondition;
+
+/**
+ * Fake implementation of {@link CommunalCondition}, and provides a way for tests to update
+ * condition fulfillment.
+ */
+public class FakeCommunalCondition extends CommunalCondition {
+    @Override
+    public void start() {}
+
+    @Override
+    public void stop() {}
+
+    public void fakeUpdateCondition(boolean isConditionMet) {
+        updateCondition(isConditionMet);
+    }
+}