diff options
author | Oscar Shu <xshu@google.com> | 2019-06-20 13:35:18 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-06-20 13:35:18 -0700 |
commit | a0626ea43dc1d9e5ae5fe6514eebf7b3d7a8d471 (patch) | |
tree | d2eef66cfdfc766b37e5bf83c7455a3073c955a3 | |
parent | 0c70bee1f091bee84bf54551904099769a543dab (diff) | |
parent | 00398ec6ea8334a706a046e723d4fe8b2855adf2 (diff) |
Merge "Trigger bugreport for abnormally long connections" into qt-r1-dev
am: 00398ec6ea
Change-Id: Ie3385594ae86fc7e49b77e689e04dec77f36a5e2
6 files changed, 266 insertions, 7 deletions
diff --git a/service/Android.mk b/service/Android.mk index b396214c7..ab005867c 100644 --- a/service/Android.mk +++ b/service/Android.mk @@ -48,7 +48,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ android.hardware.wifi.supplicant-V1.2-java \ wifi_service_proto \ ksoap2 \ - libnanohttpd + libnanohttpd \ + gsf-client LOCAL_REQUIRED_MODULES := \ services \ diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index a18760c17..292e902bc 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -959,6 +959,8 @@ public class ClientModeImpl extends StateMachine { mWifiMetrics.getHandler()); mWifiMonitor.registerHandler(mInterfaceName, CMD_TARGET_BSSID, mWifiMetrics.getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_CONNECTION_EVENT, + mWifiInjector.getWifiLastResortWatchdog().getHandler()); } private void setMulticastFilter(boolean enabled) { @@ -4292,6 +4294,7 @@ public class ClientModeImpl extends StateMachine { } if (mWifiNative.connectToNetwork(mInterfaceName, config)) { + mWifiInjector.getWifiLastResortWatchdog().noteStartConnectTime(); mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config); mLastConnectAttemptTimestamp = mClock.getWallClockMillis(); mTargetWifiConfiguration = config; diff --git a/service/java/com/android/server/wifi/GservicesFacade.java b/service/java/com/android/server/wifi/GservicesFacade.java new file mode 100644 index 000000000..a629a584d --- /dev/null +++ b/service/java/com/android/server/wifi/GservicesFacade.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 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.server.wifi; + +import android.content.Context; + +import com.google.android.gsf.Gservices; + +import java.util.concurrent.TimeUnit; + +/** + * This class allows getting all configurable flags from Gservices. + */ +public class GservicesFacade { + private static final int DEFAULT_ABNORMAL_CONNECTION_DURATION_MS = + (int) TimeUnit.SECONDS.toMillis(30); + private static final String G_PREFIX = "android.wifi."; + private Context mContext; + + public GservicesFacade(Context context) { + mContext = context; + } + + /** + * Gets the feature flag for reporting abnormally long connections. + */ + public boolean isAbnormalConnectionBugreportEnabled() { + return Gservices.getBoolean(mContext.getContentResolver(), + G_PREFIX + "abnormal_connection_bugreport_enabled", false); + } + + /** + * Gets the threshold for classifying abnormally long connections. + */ + public int getAbnormalConnectionDurationMs() { + return Gservices.getInt(mContext.getContentResolver(), + G_PREFIX + "abnormal_connection_duration_ms", + DEFAULT_ABNORMAL_CONNECTION_DURATION_MS); + } +} diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index f33c7cf93..a449ef332 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -82,6 +82,7 @@ public class WifiInjector { private final Context mContext; private final FrameworkFacade mFrameworkFacade = new FrameworkFacade(); + private final GservicesFacade mGservicesFacade; private final HandlerThread mWifiServiceHandlerThread; private final HandlerThread mWifiCoreHandlerThread; private final HandlerThread mWifiP2pServiceHandlerThread; @@ -168,6 +169,7 @@ public class WifiInjector { sWifiInjector = this; mContext = context; + mGservicesFacade = new GservicesFacade(mContext); mWifiScoreCard = new WifiScoreCard(mClock, Secure.getString(mContext.getContentResolver(), Secure.ANDROID_ID)); mSettingsStore = new WifiSettingsStore(mContext); @@ -605,8 +607,9 @@ public class WifiInjector { mWifiCoreHandlerThread.getLooper(), mFrameworkFacade, mClock, mWifiMetrics, mWifiConfigManager, mWifiConfigStore, clientModeImpl, new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade)); - mWifiLastResortWatchdog = new WifiLastResortWatchdog(this, mClock, - mWifiMetrics, clientModeImpl, clientModeImpl.getHandler().getLooper()); + mWifiLastResortWatchdog = new WifiLastResortWatchdog(this, mContext, mClock, + mWifiMetrics, clientModeImpl, clientModeImpl.getHandler().getLooper(), + mGservicesFacade); return new WifiConnectivityManager(mContext, getScoringParams(), clientModeImpl, this, mWifiConfigManager, clientModeImpl.getWifiInfo(), diff --git a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java index 6889b5016..868a3e9a5 100644 --- a/service/java/com/android/server/wifi/WifiLastResortWatchdog.java +++ b/service/java/com/android/server/wifi/WifiLastResortWatchdog.java @@ -16,10 +16,15 @@ package com.android.server.wifi; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.os.Handler; import android.os.Looper; +import android.os.Message; import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; @@ -27,6 +32,8 @@ import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; +import com.google.android.gsf.Gservices; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; @@ -75,6 +82,10 @@ public class WifiLastResortWatchdog { @VisibleForTesting public static final long LAST_TRIGGER_TIMEOUT_MILLIS = 2 * 3600 * 1000; // 2 hours + private int mAbnormalConnectionDurationMs; + private boolean mAbnormalConnectionBugreportEnabled; + + /** * Cached WifiConfigurations of available networks seen within MAX_BSSID_AGE scan results * Key:BSSID, Value:Counters of failure types @@ -102,22 +113,98 @@ public class WifiLastResortWatchdog { private Looper mClientModeImplLooper; private double mBugReportProbability = PROB_TAKE_BUGREPORT_DEFAULT; private Clock mClock; + private Context mContext; + private GservicesFacade mGservicesFacade; // If any connection failure happened after watchdog triggering restart then assume watchdog // did not fix the problem private boolean mWatchdogFixedWifi = true; + private long mLastStartConnectTime = 0; + private Handler mHandler; /** * Local log used for debugging any WifiLastResortWatchdog issues. */ private final LocalLog mLocalLog = new LocalLog(100); - WifiLastResortWatchdog(WifiInjector wifiInjector, Clock clock, WifiMetrics wifiMetrics, - ClientModeImpl clientModeImpl, Looper clientModeImplLooper) { + WifiLastResortWatchdog(WifiInjector wifiInjector, Context context, Clock clock, + WifiMetrics wifiMetrics, ClientModeImpl clientModeImpl, Looper clientModeImplLooper, + GservicesFacade gservicesFacade) { mWifiInjector = wifiInjector; mClock = clock; mWifiMetrics = wifiMetrics; mClientModeImpl = clientModeImpl; mClientModeImplLooper = clientModeImplLooper; + mContext = context; + mGservicesFacade = gservicesFacade; + updateGServicesFlags(); + mHandler = new Handler(clientModeImplLooper) { + public void handleMessage(Message msg) { + processMessage(msg); + } + }; + // Registers a broadcast receiver to change update G service flags + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mHandler.post(() -> { + updateGServicesFlags(); + }); + } + }, + new IntentFilter(Gservices.CHANGED_ACTION)); + } + + private void updateGServicesFlags() { + mAbnormalConnectionBugreportEnabled = + mGservicesFacade.isAbnormalConnectionBugreportEnabled(); + mAbnormalConnectionDurationMs = + mGservicesFacade.getAbnormalConnectionDurationMs(); + logv("updateGServicesFlags: mAbnormalConnectionDurationMs = " + + mAbnormalConnectionDurationMs + + ", mAbnormalConnectionBugreportEnabled = " + + mAbnormalConnectionBugreportEnabled); + } + + /** + * Returns handler for L2 events from supplicant. + * @return Handler + */ + public Handler getHandler() { + return mHandler; + } + + /** + * Refreshes when the last CMD_START_CONNECT is triggered. + */ + public void noteStartConnectTime() { + mLastStartConnectTime = mClock.getElapsedSinceBootMillis(); + } + + private void processMessage(Message msg) { + switch (msg.what) { + case WifiMonitor.NETWORK_CONNECTION_EVENT: + // Trigger bugreport for successful connections that take abnormally long + if (mAbnormalConnectionBugreportEnabled && mLastStartConnectTime > 0) { + long durationMs = mClock.getElapsedSinceBootMillis() - mLastStartConnectTime; + if (durationMs > mAbnormalConnectionDurationMs) { + final String bugTitle = "Wi-Fi Bugreport: Abnormal connection time"; + final String bugDetail = "Expected connection to take less than " + + mAbnormalConnectionDurationMs + " milliseconds. " + + "Actually took " + durationMs + " milliseconds."; + logv("Triggering bug report for abnormal connection time."); + mWifiInjector.getClientModeImplHandler().post(() -> { + mClientModeImpl.takeBugReport(bugTitle, bugDetail); + }); + } + } + // Should reset last connection time after each connection regardless if bugreport + // is enabled or not. + mLastStartConnectTime = 0; + break; + default: + return; + } } /** diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java index 9ae382640..1117339c6 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java @@ -21,16 +21,24 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.*; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.IntentFilter; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiSsid; +import android.os.Handler; import android.os.test.TestLooper; import android.util.Pair; import androidx.test.filters.SmallTest; +import com.google.android.gsf.Gservices; + import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import java.util.ArrayList; @@ -42,6 +50,8 @@ import java.util.List; */ @SmallTest public class WifiLastResortWatchdogTest { + final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); WifiLastResortWatchdog mLastResortWatchdog; @Mock WifiInjector mWifiInjector; @Mock WifiMetrics mWifiMetrics; @@ -49,6 +59,8 @@ public class WifiLastResortWatchdogTest { @Mock ClientModeImpl mClientModeImpl; @Mock Clock mClock; @Mock WifiInfo mWifiInfo; + @Mock Context mContext; + @Mock GservicesFacade mGservicesFacade; private String[] mSsids = {"\"test1\"", "\"test2\"", "\"test3\"", "\"test4\""}; private String[] mBssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55", @@ -61,17 +73,22 @@ public class WifiLastResortWatchdogTest { private boolean[] mHasEverConnected = {false, false, false, false}; private TestLooper mLooper; private static final String TEST_NETWORK_SSID = "\"test_ssid\""; + private static final int DEFAULT_ABNORMAL_CONNECTION_DURATION_MS = 30000; @Before public void setUp() throws Exception { initMocks(this); mLooper = new TestLooper(); when(mWifiInjector.getSelfRecovery()).thenReturn(mSelfRecovery); - mLastResortWatchdog = new WifiLastResortWatchdog(mWifiInjector, mClock, mWifiMetrics, - mClientModeImpl, mLooper.getLooper()); + when(mGservicesFacade.isAbnormalConnectionBugreportEnabled()).thenReturn(true); + when(mGservicesFacade.getAbnormalConnectionDurationMs()).thenReturn( + DEFAULT_ABNORMAL_CONNECTION_DURATION_MS); + mLastResortWatchdog = new WifiLastResortWatchdog(mWifiInjector, mContext, mClock, + mWifiMetrics, mClientModeImpl, mLooper.getLooper(), mGservicesFacade); mLastResortWatchdog.setBugReportProbability(1); when(mClientModeImpl.getWifiInfo()).thenReturn(mWifiInfo); when(mWifiInfo.getSSID()).thenReturn(TEST_NETWORK_SSID); + when(mWifiInjector.getClientModeImplHandler()).thenReturn(mLastResortWatchdog.getHandler()); } private List<Pair<ScanDetail, WifiConfiguration>> createFilteredQnsCandidates(String[] ssids, @@ -2152,4 +2169,98 @@ public class WifiLastResortWatchdogTest { verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogSuccesses(); } + /** + * Verifies that when a connection takes too long (time difference between + * StaEvent.TYPE_CMD_START_CONNECT and StaEvent.TYPE_NETWORK_CONNECTION_EVENT) a bugreport is + * taken. + */ + @Test + public void testAbnormalConnectionTimeTriggersBugreport() throws Exception { + // first verifies that bugreports are not taken when connection takes less than + // DEFAULT_ABNORMAL_CONNECTION_DURATION_MS + when(mClock.getElapsedSinceBootMillis()).thenReturn(1L); + mLastResortWatchdog.noteStartConnectTime(); + when(mClock.getElapsedSinceBootMillis()).thenReturn( + (long) DEFAULT_ABNORMAL_CONNECTION_DURATION_MS); + Handler handler = mLastResortWatchdog.getHandler(); + handler.sendMessage( + handler.obtainMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null)); + mLooper.dispatchAll(); + verify(mClientModeImpl, never()).takeBugReport(anyString(), anyString()); + + // Now verify that bugreport is taken + mLastResortWatchdog.noteStartConnectTime(); + when(mClock.getElapsedSinceBootMillis()).thenReturn( + 2L * DEFAULT_ABNORMAL_CONNECTION_DURATION_MS + 1); + handler.sendMessage( + handler.obtainMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null)); + mLooper.dispatchAll(); + verify(mClientModeImpl).takeBugReport(anyString(), anyString()); + + // Verify additional connections (without more TYPE_CMD_START_CONNECT) don't trigger more + // bugreports. + when(mClock.getElapsedSinceBootMillis()).thenReturn( + 4L * DEFAULT_ABNORMAL_CONNECTION_DURATION_MS); + handler.sendMessage( + handler.obtainMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null)); + mLooper.dispatchAll(); + verify(mClientModeImpl).takeBugReport(anyString(), anyString()); + } + + /** + * Changes |mAbnormalConnectionDurationMs| to a new value, and then verify that a bugreport is + * taken for a connection that takes longer than the new threshold. + * @throws Exception + */ + @Test + public void testGServicesSetDuration() throws Exception { + final int testDurationMs = 10 * 1000; // 10 seconds + // changes the abnormal connection duration to |testDurationMs|. + when(mGservicesFacade.getAbnormalConnectionDurationMs()).thenReturn(testDurationMs); + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + (IntentFilter) argThat(new IntentFilterMatcher())); + mBroadcastReceiverCaptor.getValue().onReceive(mContext, null); + + // verifies that bugreport is taken for connections that take longer than |testDurationMs|. + when(mClock.getElapsedSinceBootMillis()).thenReturn(1L); + mLastResortWatchdog.noteStartConnectTime(); + when(mClock.getElapsedSinceBootMillis()).thenReturn((long) testDurationMs + 2); + Handler handler = mLastResortWatchdog.getHandler(); + handler.sendMessage( + handler.obtainMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null)); + mLooper.dispatchAll(); + verify(mClientModeImpl).takeBugReport(anyString(), anyString()); + } + + /** + * Verifies that bugreports are not triggered even when conditions are met after the + * |mAbnormalConnectionBugreportEnabled| flag is changed to false. + * @throws Exception + */ + @Test + public void testGServicesFlagDisable() throws Exception { + // changes |mAbnormalConnectionBugreportEnabled| to false. + when(mGservicesFacade.isAbnormalConnectionBugreportEnabled()).thenReturn(false); + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + (IntentFilter) argThat(new IntentFilterMatcher())); + mBroadcastReceiverCaptor.getValue().onReceive(mContext, null); + + // verifies that bugreports are not taken. + when(mClock.getElapsedSinceBootMillis()).thenReturn(1L); + mLastResortWatchdog.noteStartConnectTime(); + when(mClock.getElapsedSinceBootMillis()).thenReturn( + (long) DEFAULT_ABNORMAL_CONNECTION_DURATION_MS + 2); + Handler handler = mLastResortWatchdog.getHandler(); + handler.sendMessage( + handler.obtainMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null)); + mLooper.dispatchAll(); + verify(mClientModeImpl, never()).takeBugReport(anyString(), anyString()); + } + + private class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> { + @Override + public boolean matches(IntentFilter filter) { + return filter.hasAction(Gservices.CHANGED_ACTION); + } + } } |