Synchronize access to mCurrentState when updating

WifiSignalController expects broadcast updates to come in off the main
thread, and WifiStatusTracker updates to come in on the main thread.

This change ensures data access synchronization by making sure that all
data access happens on a known background thread.

Bug: 229425925
Test: atest NetworkControllerWifiTest
Change-Id: Ifc04b2e25b0cc08ad6406030fa9de3df0ba2df2d
This commit is contained in:
Evan Laird
2022-04-25 13:18:10 -04:00
parent 3d790a1673
commit ef3d71dfb4
7 changed files with 116 additions and 42 deletions

View File

@@ -33,7 +33,6 @@ import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkScoreManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
@@ -225,10 +224,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
TelephonyManager telephonyManager,
TelephonyListenerManager telephonyListenerManager,
@Nullable WifiManager wifiManager,
NetworkScoreManager networkScoreManager,
AccessPointControllerImpl accessPointController,
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
WifiStatusTrackerFactory trackerFactory,
@Main Handler handler,
InternetDialogFactory internetDialogFactory,
FeatureFlags featureFlags,
@@ -237,7 +236,6 @@ public class NetworkControllerImpl extends BroadcastReceiver
telephonyManager,
telephonyListenerManager,
wifiManager,
networkScoreManager,
subscriptionManager,
Config.readConfig(context),
bgLooper,
@@ -250,6 +248,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
broadcastDispatcher,
demoModeController,
carrierConfigTracker,
trackerFactory,
handler,
featureFlags,
dumpManager);
@@ -262,8 +261,9 @@ public class NetworkControllerImpl extends BroadcastReceiver
TelephonyManager telephonyManager,
TelephonyListenerManager telephonyListenerManager,
WifiManager wifiManager,
NetworkScoreManager networkScoreManager,
SubscriptionManager subManager, Config config, Looper bgLooper,
SubscriptionManager subManager,
Config config,
Looper bgLooper,
Executor bgExecutor,
CallbackHandler callbackHandler,
AccessPointControllerImpl accessPointController,
@@ -273,6 +273,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
BroadcastDispatcher broadcastDispatcher,
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
WifiStatusTrackerFactory trackerFactory,
@Main Handler handler,
FeatureFlags featureFlags,
DumpManager dumpManager
@@ -315,9 +316,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
notifyControllersMobileDataChanged();
}
});
mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
mCallbackHandler, this, mWifiManager, mConnectivityManager, networkScoreManager,
mMainHandler, mReceiverHandler);
mCallbackHandler, this, mWifiManager, trackerFactory,
mReceiverHandler);
mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this);

View File

@@ -50,7 +50,7 @@ public abstract class SignalController<T extends ConnectivityState, I extends Ic
protected final T mLastState;
protected final int mTransportType;
protected final Context mContext;
// The owner of the SignalController (i.e. NetworkController will maintain the following
// The owner of the SignalController (i.e. NetworkController) will maintain the following
// lists and call notifyListeners whenever the list has changed to ensure everyone
// is aware of current state.
protected final NetworkControllerImpl mNetworkController;
@@ -103,7 +103,7 @@ public abstract class SignalController<T extends ConnectivityState, I extends Ic
* Determines if the state of this signal controller has changed and
* needs to trigger callbacks related to it.
*/
public boolean isDirty() {
boolean isDirty() {
if (!mLastState.equals(mCurrentState)) {
if (DEBUG) {
Log.d(mTag, "Change in state from: " + mLastState + "\n"

View File

@@ -21,14 +21,13 @@ import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OU
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.text.Html;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.settingslib.SignalIcon.IconGroup;
import com.android.settingslib.SignalIcon.MobileIconGroup;
import com.android.settingslib.graph.SignalDrawable;
@@ -36,8 +35,6 @@ import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.wifi.WifiStatusTracker;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.Assert;
import java.io.PrintWriter;
@@ -49,21 +46,21 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
private final MobileIconGroup mCarrierMergedWifiIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI;
private final WifiManager mWifiManager;
private final Handler mBgHandler;
public WifiSignalController(
Context context,
boolean hasMobileDataFeature,
CallbackHandler callbackHandler,
NetworkControllerImpl networkController,
WifiManager wifiManager,
ConnectivityManager connectivityManager,
NetworkScoreManager networkScoreManager,
@Main Handler handler,
@Background Handler backgroundHandler) {
WifiStatusTrackerFactory trackerFactory,
@Background Handler bgHandler) {
super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
callbackHandler, networkController);
mBgHandler = bgHandler;
mWifiManager = wifiManager;
mWifiTracker = new WifiStatusTracker(mContext, wifiManager, networkScoreManager,
connectivityManager, this::handleStatusUpdated, handler, backgroundHandler);
mWifiTracker = trackerFactory.createTracker(this::handleStatusUpdated, bgHandler);
mWifiTracker.setListening(true);
mHasMobileDataFeature = hasMobileDataFeature;
if (wifiManager != null) {
@@ -181,33 +178,51 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
* Fetches wifi initial state replacing the initial sticky broadcast.
*/
public void fetchInitialState() {
mWifiTracker.fetchInitialState();
copyWifiStates();
notifyListenersIfNecessary();
doInBackground(() -> {
mWifiTracker.fetchInitialState();
copyWifiStates();
notifyListenersIfNecessary();
});
}
/**
* Extract wifi state directly from broadcasts about changes in wifi state.
*/
public void handleBroadcast(Intent intent) {
mWifiTracker.handleBroadcast(intent);
copyWifiStates();
notifyListenersIfNecessary();
void handleBroadcast(Intent intent) {
doInBackground(() -> {
mWifiTracker.handleBroadcast(intent);
copyWifiStates();
notifyListenersIfNecessary();
});
}
private void handleStatusUpdated() {
Assert.isMainThread();
copyWifiStates();
notifyListenersIfNecessary();
// The WifiStatusTracker callback comes in on the main thread, but the rest of our data
// access happens on the bgHandler
doInBackground(() -> {
copyWifiStates();
notifyListenersIfNecessary();
});
}
private void doInBackground(Runnable action) {
if (Thread.currentThread() != mBgHandler.getLooper().getThread()) {
mBgHandler.post(action);
} else {
action.run();
}
}
private void copyWifiStates() {
// Data access should only happen on our bg thread
Preconditions.checkState(mBgHandler.getLooper().isCurrentThread());
mCurrentState.enabled = mWifiTracker.enabled;
mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
mCurrentState.connected = mWifiTracker.connected;
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
notifyWifiLevelChangeIfNecessary(mWifiTracker.level);
boolean levelChanged = mCurrentState.level != mWifiTracker.level;
mCurrentState.level = mWifiTracker.level;
mCurrentState.statusLabel = mWifiTracker.statusLabel;
mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged;
@@ -215,11 +230,9 @@ public class WifiSignalController extends SignalController<WifiState, IconGroup>
mCurrentState.iconGroup =
mCurrentState.isCarrierMerged ? mCarrierMergedWifiIconGroup
: mUnmergedWifiIconGroup;
}
void notifyWifiLevelChangeIfNecessary(int level) {
if (level != mCurrentState.level) {
mNetworkController.notifyWifiLevelChange(level);
if (levelChanged) {
mNetworkController.notifyWifiLevelChange(mCurrentState.level);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2015 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.statusbar.connectivity
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkScoreManager
import android.net.wifi.WifiManager
import android.os.Handler
import com.android.settingslib.wifi.WifiStatusTracker
import com.android.systemui.dagger.qualifiers.Main
import javax.inject.Inject
/**
* Factory class for [WifiStatusTracker] which lives in SettingsLib (and thus doesn't use Dagger).
* This enables the constructors for NetworkControllerImpl and WifiSignalController to be slightly
* nicer.
*/
internal class WifiStatusTrackerFactory @Inject constructor(
private val mContext: Context,
private val mWifiManager: WifiManager?,
private val mNetworkScoreManager: NetworkScoreManager,
private val mConnectivityManager: ConnectivityManager,
@Main private val mMainHandler: Handler
) {
fun createTracker(callback: Runnable?, bgHandler: Handler?): WifiStatusTracker {
return WifiStatusTracker(mContext,
mWifiManager,
mNetworkScoreManager,
mConnectivityManager,
callback,
mMainHandler,
bgHandler)
}
}

View File

@@ -127,6 +127,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
protected Handler mMainHandler;
protected FeatureFlags mFeatureFlags;
protected WifiStatusTrackerFactory mWifiStatusTrackerFactory;
protected int mSubId;
@@ -220,12 +221,14 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
return null;
}).when(mMockProvisionController).addCallback(any());
mWifiStatusTrackerFactory = new WifiStatusTrackerFactory(
mContext, mMockWm, mMockNsm, mMockCm, mMainHandler);
mNetworkController = new NetworkControllerImpl(mContext,
mMockCm,
mMockTm,
mTelephonyListenerManager,
mMockWm,
mMockNsm,
mMockSm,
mConfig,
TestableLooper.get(this).getLooper(),
@@ -238,6 +241,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase {
mMockBd,
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
mMainHandler,
mFeatureFlags,
mock(DumpManager.class)

View File

@@ -127,11 +127,13 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
mConfig.show4gForLte = true;
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
mTelephonyListenerManager, mMockWm,
mMockNsm, mMockSm, mConfig, Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mMockSm, mConfig, Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class),
mock(DataUsageController.class), mMockSubDefaults,
mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
mock(CarrierConfigTracker.class), new Handler(TestableLooper.get(this).getLooper()),
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
new Handler(TestableLooper.get(this).getLooper()),
mFeatureFlags, mock(DumpManager.class));
setupNetworkController();

View File

@@ -71,7 +71,6 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mMockTm,
mTelephonyListenerManager,
mMockWm,
mMockNsm,
mMockSm,
mConfig,
TestableLooper.get(this).getLooper(),
@@ -84,6 +83,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mMockBd,
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
mMainHandler,
mFeatureFlags,
mock(DumpManager.class)
@@ -105,7 +105,6 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mMockTm,
mTelephonyListenerManager,
mMockWm,
mMockNsm,
mMockSm,
mConfig,
TestableLooper.get(this).getLooper(),
@@ -118,6 +117,7 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
mMockBd,
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
mMainHandler,
mFeatureFlags,
mock(DumpManager.class)
@@ -134,11 +134,12 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
when(mMockTm.isDataCapable()).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
mTelephonyListenerManager, mMockWm, mMockNsm, mMockSm, mConfig,
mTelephonyListenerManager, mMockWm, mMockSm, mConfig,
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
mDemoModeController, mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
mMainHandler, mFeatureFlags,
mock(DumpManager.class));
setupNetworkController();
@@ -156,11 +157,12 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
when(mMockSm.getCompleteActiveSubscriptionInfoList()).thenReturn(Collections.emptyList());
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
mTelephonyListenerManager, mMockWm, mMockNsm, mMockSm, mConfig,
mTelephonyListenerManager, mMockWm, mMockSm, mConfig,
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
mDemoModeController, mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
mMainHandler, mFeatureFlags, mock(DumpManager.class));
mNetworkController.registerListeners();
@@ -225,11 +227,12 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
when(mMockTm.isDataCapable()).thenReturn(false);
// Create a new NetworkController as this is currently handled in constructor.
mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm,
mTelephonyListenerManager, mMockWm, mMockNsm, mMockSm, mConfig,
mTelephonyListenerManager, mMockWm, mMockSm, mConfig,
Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
mDemoModeController, mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
mMainHandler, mFeatureFlags, mock(DumpManager.class));
setupNetworkController();