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

Commit 4a0a95ba authored by Darrell Shi's avatar Darrell Shi Committed by Android (Google) Code Review
Browse files

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.
parents 846b87f2 d1933210
Loading
Loading
Loading
Loading
+31 −42
Original line number Original line Diff line number Diff line
@@ -16,17 +16,11 @@


package com.android.systemui.communal;
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 android.util.Log;


import androidx.annotation.MainThread;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.util.settings.SecureSettings;


import com.google.android.collect.Lists;
import com.google.android.collect.Lists;


@@ -46,10 +40,15 @@ public class CommunalSourceMonitor {


    // A list of {@link Callback} that have registered to receive updates.
    // A list of {@link Callback} that have registered to receive updates.
    private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
    private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
    private final SecureSettings mSecureSettings;
    private final CommunalConditionsMonitor mConditionsMonitor;


    private CommunalSource mCurrentSource;
    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() {
    private CommunalSource.Callback mSourceCallback = new CommunalSource.Callback() {
        @Override
        @Override
@@ -59,24 +58,20 @@ public class CommunalSourceMonitor {
        }
        }
    };
    };


    @VisibleForTesting
    private final CommunalConditionsMonitor.Callback mConditionsCallback =
    @Inject
            allConditionsMet -> {
    public CommunalSourceMonitor(
                if (mAllCommunalConditionsMet != allConditionsMet) {
            @MainThread Handler mainThreadHandler,
                    if (DEBUG) Log.d(TAG, "communal conditions changed: " + allConditionsMet);
            SecureSettings secureSettings) {
        mSecureSettings = secureSettings;


        ContentObserver settingsObserver = new ContentObserver(mainThreadHandler) {
                    mAllCommunalConditionsMet = allConditionsMet;
            @Override
                    executeOnSourceAvailableCallbacks();
            public void onChange(boolean selfChange) {
                reloadSettings();
                }
                }
            };
            };
        mSecureSettings.registerContentObserverForUser(

                Settings.Secure.COMMUNAL_MODE_ENABLED,
    @VisibleForTesting
                /* notifyForDescendants= */false,
    @Inject
                settingsObserver, UserHandle.USER_SYSTEM);
    public CommunalSourceMonitor(CommunalConditionsMonitor communalConditionsMonitor) {
        reloadSettings();
        mConditionsMonitor = communalConditionsMonitor;
    }
    }


    /**
    /**
@@ -92,7 +87,7 @@ public class CommunalSourceMonitor {


        mCurrentSource = source;
        mCurrentSource = source;


        if (mCommunalEnabled) {
        if (mAllCommunalConditionsMet) {
            executeOnSourceAvailableCallbacks();
            executeOnSourceAvailableCallbacks();
        }
        }


@@ -111,7 +106,7 @@ public class CommunalSourceMonitor {
                itr.remove();
                itr.remove();
            } else {
            } else {
                cb.onSourceAvailable(
                cb.onSourceAvailable(
                        (mCommunalEnabled && mCurrentSource != null) ? new WeakReference<>(
                        (mAllCommunalConditionsMet && mCurrentSource != null) ? new WeakReference<>(
                                mCurrentSource) : null);
                                mCurrentSource) : null);
            }
            }
        }
        }
@@ -126,9 +121,14 @@ public class CommunalSourceMonitor {
        mCallbacks.add(new WeakReference<>(callback));
        mCallbacks.add(new WeakReference<>(callback));


        // Inform the callback of any already present CommunalSource.
        // Inform the callback of any already present CommunalSource.
        if (mCommunalEnabled && mCurrentSource != null) {
        if (mAllCommunalConditionsMet && mCurrentSource != null) {
            callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
            callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
        }
        }

        if (!mListeningForConditions) {
            mConditionsMonitor.addCallback(mConditionsCallback);
            mListeningForConditions = true;
        }
    }
    }


    /**
    /**
@@ -138,21 +138,10 @@ public class CommunalSourceMonitor {
     */
     */
    public void removeCallback(Callback callback) {
    public void removeCallback(Callback callback) {
        mCallbacks.removeIf(el -> el.get() == callback);
        mCallbacks.removeIf(el -> el.get() == callback);
    }


    private void reloadSettings() {
        if (mCallbacks.isEmpty() && mListeningForConditions) {
        boolean newCommunalEnabled = mSecureSettings.getIntForUser(
            mConditionsMonitor.removeCallback(mConditionsCallback);
                Settings.Secure.COMMUNAL_MODE_ENABLED,
            mListeningForConditions = false;
                1,
                UserHandle.USER_SYSTEM) == 1;

        if (DEBUG) {
            Log.d(TAG, "communal mode settings reloaded with value:" + newCommunalEnabled);
        }

        if (mCommunalEnabled != newCommunalEnabled) {
            mCommunalEnabled = newCommunalEnabled;
            executeOnSourceAvailableCallbacks();
        }
        }
    }
    }


+130 −0
Original line number Original line 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.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);
    }
}
+154 −0
Original line number Original line 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 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);
    }
}
+66 −0
Original line number Original line 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.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);
    }
}
+189 −0
Original line number Original line 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.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);
    }
}
Loading