blob: d380681ed1c8d8d9cd7ec5b9b335fb99913e2563 [file] [log] [blame]
package com.android.clockwork.connectivity;
import static android.net.ConnectivityManager.NetworkCallback;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import static org.robolectric.shadows.ShadowAlarmManager.ScheduledAlarm;
import android.app.AlarmManager;
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.Network;
import android.os.SystemClock;
import androidx.test.core.app.ApplicationProvider;
import com.android.clockwork.bluetooth.WearBluetoothMediator;
import com.android.clockwork.cellular.WearCellularMediator;
import com.android.clockwork.common.ActivityModeTracker;
import com.android.clockwork.common.CellOnlyMode;
import com.android.clockwork.common.ProxyConnectivityDebounce.ProxyStatus;
import com.android.clockwork.common.ThermalEmergencyTracker;
import com.android.clockwork.common.WearResourceUtil;
import com.android.clockwork.wifi.WearWifiMediator;
import com.android.wearable.resources.R;
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 org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowAlarmManager;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowPackageManager;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class WearConnectivityControllerTest {
final ShadowApplication shadowApplication = ShadowApplication.getInstance();
private ShadowAlarmManager mShadowAlarmManager;
@Mock WearBluetoothMediator mockBtMediator;
@Mock WearWifiMediator mockWifiMediator;
@Mock WearCellularMediator mockCellMediator;
@Mock WearConnectivityPackageManager mockWearConnectivityPackageManager;
@Mock WearProxyNetworkAgent mockProxyNetworkAgent;
@Mock ActivityModeTracker mockActivityModeTracker;
@Mock CellOnlyMode mockCellOnlyMode;
@Mock Resources mMockWearResources;
@Mock ThermalEmergencyTracker mMockThermalEmergencyTracker;
@Captor
ArgumentCaptor<WearBluetoothMediator.ProxyStatusListener> mProxyStatusListenerCaptor;
@Captor
ArgumentCaptor<AlarmManager.OnAlarmListener> mAlarmListenerCaptor;
WearConnectivityController mController;
private static final long TEST_START_TIME = 1000000L;
private static final int TEST_WIFI_DELAY = 50;
private static final int TEST_CELL_DELAY = 30000;
private static final int TEST_EXTEND_CELL = 999000;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
SystemClock.setCurrentTimeMillis(TEST_START_TIME);
Context context = ApplicationProvider.getApplicationContext();
shadowOf(context.getPackageManager()).addPackage(WearResourceUtil.WEAR_RESOURCE_PACKAGE);
ShadowPackageManager.resources.put(WearResourceUtil.WEAR_RESOURCE_PACKAGE,
mMockWearResources);
when(mMockWearResources.getInteger(R.integer.proxy_connectivity_delay_wifi)).thenReturn(
TEST_WIFI_DELAY);
when(mMockWearResources.getInteger(R.integer.proxy_connectivity_delay_cell)).thenReturn(
TEST_CELL_DELAY);
when(mMockWearResources.getInteger(R.integer.wifi_connectivity_extend_cell_delay))
.thenReturn(TEST_EXTEND_CELL);
AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
mShadowAlarmManager = shadowOf(alarmManager);
mController = new WearConnectivityController(
RuntimeEnvironment.application,
alarmManager,
mockBtMediator,
mockWifiMediator,
mockCellMediator,
mockWearConnectivityPackageManager,
mockProxyNetworkAgent,
mockActivityModeTracker,
mockCellOnlyMode,
mMockThermalEmergencyTracker);
verify(mockActivityModeTracker).addListener(mController);
verify(mockCellOnlyMode).addListener(mController);
verify(mockBtMediator).setProxyStatusListener(mProxyStatusListenerCaptor.capture());
// initial controller state
mController.onBootCompleted();
}
@Test
public void testOnBootCompleted_proxyDisconnected() {
reset(mockBtMediator, mockWifiMediator, mockCellMediator);
when(mockBtMediator.isProxyConnected()).thenReturn(false);
mController.onBootCompleted();
verify(mockBtMediator).onBootCompleted();
verify(mockWifiMediator).onBootCompleted(false);
verify(mockCellMediator).onBootCompleted(false);
}
@Test
public void testOnBootCompleted_proxyConnected() {
reset(mockBtMediator, mockWifiMediator, mockCellMediator);
when(mockBtMediator.isProxyConnected()).thenReturn(true);
mController.onBootCompleted();
verify(mockBtMediator).onBootCompleted();
verify(mockWifiMediator).onBootCompleted(true);
verify(mockCellMediator).onBootCompleted(true);
}
@Test
public void testProxyConnectionStateForwarding_connected() {
mProxyStatusListenerCaptor.getValue().onProxyStatusChange(
new ProxyStatus(true, true, true));
verify(mockWifiMediator).onProxyConnectedChange(true);
verify(mockCellMediator).onProxyConnectedChange(true);
}
@Test
public void testProxyConnectionStateForwarding_disabled() {
mProxyStatusListenerCaptor.getValue().onProxyStatusChange(
new ProxyStatus(false, false, false));
verify(mockWifiMediator).onProxyConnectedChange(false);
verify(mockCellMediator).onProxyConnectedChange(false);
}
@Test
public void testProxyConnectionStateForwarding_disconnected_debounced() {
mProxyStatusListenerCaptor.getValue().onProxyStatusChange(
new ProxyStatus(false, true, false));
mProxyStatusListenerCaptor.getValue().onProxyStatusChange(
new ProxyStatus(true, true, true));
verify(mockWifiMediator, never()).onProxyConnectedChange(false);
verify(mockCellMediator, never()).onProxyConnectedChange(false);
}
@Test
public void testProxyConnectionStateForwarding_disconnected() {
mProxyStatusListenerCaptor.getValue().onProxyStatusChange(
new ProxyStatus(false, true, false));
verify(mockWifiMediator, never()).onProxyConnectedChange(anyBoolean());
verify(mockCellMediator, never()).onProxyConnectedChange(anyBoolean());
// fire alarms for Wifi delay
advanceTo(TEST_WIFI_DELAY);
verify(mockWifiMediator).onProxyConnectedChange(false);
verify(mockCellMediator, never()).onProxyConnectedChange(false);
// fire alarms for Cell delay
advanceTo(TEST_CELL_DELAY);
verify(mockCellMediator).onProxyConnectedChange(false);
}
@Test
public void testOnProxyStatusChange_disconnected_hasWifi() {
mProxyStatusListenerCaptor.getValue().onProxyStatusChange(
new ProxyStatus(false, true, false));
wifiNetworkConnectedChange(true);
advanceTo(TEST_CELL_DELAY);
verify(mockCellMediator, never()).onProxyConnectedChange(false);
advanceTo(999000L);
verify(mockCellMediator).onProxyConnectedChange(false);
}
@Test
public void testOnProxyStatusChange_disconnected_lateWifi() {
mProxyStatusListenerCaptor.getValue().onProxyStatusChange(
new ProxyStatus(false, true, false));
advanceTo(TEST_CELL_DELAY);
reset(mockCellMediator);
// debounce for cell already triggered update; late Wifi network
// connect is ignored and should not cause another proxy update.
wifiNetworkConnectedChange(true);
advanceTo(999000L);
verify(mockCellMediator, never()).onProxyConnectedChange(false);
}
@Test
public void testOnWifiConnectedChange() {
wifiNetworkConnectedChange(true);
verify(mockCellMediator).onWifiConnectedChange(true);
}
@Test
public void testOnWifiConnectedChange_disconnected() {
wifiNetworkConnectedChange(true);
wifiNetworkConnectedChange(false);
verify(mockCellMediator).onWifiConnectedChange(false);
}
@Test
public void testNetworkRequestForwarding() {
mController.onWifiRequestsChanged(1);
mController.onCellularRequestsChanged(2);
mController.onHighBandwidthRequestsChanged(3);
mController.onUnmeteredRequestsChanged(4);
verify(mockWifiMediator).updateNumWifiRequests(1);
verify(mockWifiMediator).updateNumHighBandwidthRequests(3);
verify(mockWifiMediator).updateNumUnmeteredRequests(4);
verify(mockCellMediator).updateNumCellularRequests(2);
verify(mockCellMediator).updateNumHighBandwidthRequests(3);
}
@Test
public void testActivityModeChanges() {
reset(mockWifiMediator, mockCellMediator, mockBtMediator);
when(mockActivityModeTracker.affectsBluetooth()).thenReturn(false);
when(mockActivityModeTracker.affectsWifi()).thenReturn(false);
when(mockActivityModeTracker.affectsCellular()).thenReturn(false);
mController.onActivityModeChanged(true);
verifyNoMoreInteractions(mockWifiMediator);
verifyNoMoreInteractions(mockCellMediator);
verifyNoMoreInteractions(mockBtMediator);
// TODO set up various radio matrix/configurations and test that they get toggled
}
@Test
public void testCellOnlyModeChanges() {
reset(mockWifiMediator, mockCellMediator, mockBtMediator);
mController.onCellOnlyModeChanged(true);
verify(mockBtMediator).updateCellOnlyMode(true);
verify(mockWifiMediator).updateCellOnlyMode(true);
verify(mockCellMediator).updateCellOnlyMode(true);
mController.onCellOnlyModeChanged(false);
verify(mockBtMediator).updateCellOnlyMode(false);
verify(mockWifiMediator).updateCellOnlyMode(false);
verify(mockCellMediator).updateCellOnlyMode(false);
}
private long getCurrentTimeForAlarmType(int type) {
return (type == AlarmManager.RTC || type == AlarmManager.RTC_WAKEUP)
? 0 : SystemClock.elapsedRealtime();
}
private void alarmsFired() {
// copy list since onAlarm callback can cancel and modify list of scheduled alarm.
List<ScheduledAlarm> scheduledAlarms = new ArrayList<>(
mShadowAlarmManager.getScheduledAlarms());
for (ScheduledAlarm alarm : scheduledAlarms) {
if (alarm.onAlarmListener != null
&& alarm.triggerAtTime <= getCurrentTimeForAlarmType(alarm.type)) {
alarm.onAlarmListener.onAlarm();
}
}
}
private void advanceTo(long timeMillis) {
SystemClock.setCurrentTimeMillis(TEST_START_TIME + timeMillis);
alarmsFired();
}
private void wifiNetworkConnectedChange(boolean isWifiConnected) {
Context context = ApplicationProvider.getApplicationContext();
ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
// ShadowConnectivityManager drops info about requests; here assume that only Wifi network
// request was registered for callbacks. It doesn't matter which instance of network is
// mocking wifi; pretend first instance in the list is the wifi one.
Network wifiNetwork = cm.getAllNetworks()[0];
for (NetworkCallback cb : shadowOf(cm).getNetworkCallbacks()) {
if (isWifiConnected) {
cb.onAvailable(wifiNetwork);
} else {
cb.onLost(wifiNetwork);
}
}
}
}