blob: d31d550ae90242d6f435d35fd0590a8b0c247283 [file] [log] [blame]
/*
* Copyright (C) 2011 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;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.uidPoliciesToString;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG;
import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG;
import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT;
import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED;
import static android.text.format.Time.TIMEZONE_UTC;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.IActivityManager;
import android.app.IUidObserver;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkPolicy;
import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.StringNetworkSpecifier;
import android.os.Binder;
import android.os.INetworkManagementService;
import android.os.PersistableBundle;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.RemoteException;
import android.os.SimpleClock;
import android.os.SystemClock;
import android.os.UserHandle;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.MediumTest;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.DataUnit;
import android.util.Log;
import android.util.Pair;
import android.util.RecurrenceRule;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsManagerInternal;
import libcore.io.IoUtils;
import libcore.io.Streams;
import com.google.common.util.concurrent.AbstractFuture;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.MethodRule;
import org.junit.runner.RunWith;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.time.Clock;
import java.time.Instant;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
/**
* Tests for {@link NetworkPolicyManagerService}.
*
* <p>Typical usage:
*
* <pre><code>
m -j32 FrameworksServicesTests && adb install -r -g \
${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && \
adb shell am instrument -e class "com.android.server.NetworkPolicyManagerServiceTest" -w \
"com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner"
* </code></pre>
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
public class NetworkPolicyManagerServiceTest {
private static final String TAG = "NetworkPolicyManagerServiceTest";
private static final long TEST_START = 1194220800000L;
private static final String TEST_IFACE = "test0";
private static final String TEST_SSID = "AndroidAP";
private static final String TEST_IMSI = "310210";
private static final int TEST_SUB_ID = 42;
private static final int TEST_NET_ID = 24;
private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(TEST_SSID);
/**
* Path on assets where files used by {@link NetPolicyXml} are located.
*/
private static final String NETPOLICY_DIR = "NetworkPolicyManagerServiceTest/netpolicy";
private BroadcastInterceptingContext mServiceContext;
private File mPolicyDir;
/**
* Relative path of the XML file that will be used as {@code netpolicy.xml}.
*
* <p>Typically set through a {@link NetPolicyXml} annotation in the test method.
*/
private String mNetpolicyXml;
private @Mock IActivityManager mActivityManager;
private @Mock INetworkManagementService mNetworkManager;
private @Mock IConnectivityManager mConnManager;
private @Mock NotificationManager mNotifManager;
private @Mock PackageManager mPackageManager;
private @Mock IPackageManager mIpm;
private @Mock SubscriptionManager mSubscriptionManager;
private @Mock CarrierConfigManager mCarrierConfigManager;
private @Mock TelephonyManager mTelephonyManager;
private ActivityManagerInternal mActivityManagerInternal;
private NetworkStatsManagerInternal mStatsService;
private IUidObserver mUidObserver;
private INetworkManagementEventObserver mNetworkObserver;
private NetworkPolicyListenerAnswer mPolicyListener;
private NetworkPolicyManagerService mService;
/**
* In some of the tests while initializing NetworkPolicyManagerService,
* ACTION_RESTRICT_BACKGROUND_CHANGED is broadcasted. This is for capturing that broadcast.
*/
private FutureIntent mFutureIntent;
private long mStartTime;
private long mElapsedRealtime;
private static final int USER_ID = 0;
private static final int FAKE_SUB_ID = 3737373;
private static final String FAKE_SUBSCRIBER_ID = "FAKE_SUB_ID";
private static final int DEFAULT_CYCLE_DAY = 1;
private static final int INVALID_CARRIER_CONFIG_VALUE = -9999;
private long mDefaultWarningBytes; // filled in with the actual default before tests are run
private long mDefaultLimitBytes; // filled in with the actual default before tests are run
private static final int APP_ID_A = android.os.Process.FIRST_APPLICATION_UID + 4;
private static final int APP_ID_B = android.os.Process.FIRST_APPLICATION_UID + 8;
private static final int APP_ID_C = android.os.Process.FIRST_APPLICATION_UID + 15;
private static final int APP_ID_D = android.os.Process.FIRST_APPLICATION_UID + 16;
private static final int APP_ID_E = android.os.Process.FIRST_APPLICATION_UID + 23;
private static final int APP_ID_F = android.os.Process.FIRST_APPLICATION_UID + 42;
private static final int UID_A = UserHandle.getUid(USER_ID, APP_ID_A);
private static final int UID_B = UserHandle.getUid(USER_ID, APP_ID_B);
private static final int UID_C = UserHandle.getUid(USER_ID, APP_ID_C);
private static final int UID_D = UserHandle.getUid(USER_ID, APP_ID_D);
private static final int UID_E = UserHandle.getUid(USER_ID, APP_ID_E);
private static final int UID_F = UserHandle.getUid(USER_ID, APP_ID_F);
private static final String PKG_NAME_A = "name.is.A,pkg.A";
private static final String PKG_NAME_B = "name.is.B,pkg.B";
private static final String PKG_NAME_C = "name.is.C,pkg.C";
public final @Rule NetPolicyMethodRule mNetPolicyXmlRule = new NetPolicyMethodRule();
private final Clock mClock = new SimpleClock(ZoneOffset.UTC) {
@Override
public long millis() {
return currentTimeMillis();
}
};
private void registerLocalServices() {
addLocalServiceMock(DeviceIdleController.LocalService.class);
final UsageStatsManagerInternal usageStats =
addLocalServiceMock(UsageStatsManagerInternal.class);
when(usageStats.getIdleUidsForUser(anyInt())).thenReturn(new int[]{});
mActivityManagerInternal = addLocalServiceMock(ActivityManagerInternal.class);
final PowerSaveState state = new PowerSaveState.Builder()
.setBatterySaverEnabled(false).build();
final PowerManagerInternal pmInternal = addLocalServiceMock(PowerManagerInternal.class);
when(pmInternal.getLowPowerState(anyInt())).thenReturn(state);
mStatsService = addLocalServiceMock(NetworkStatsManagerInternal.class);
}
@Before
public void callSystemReady() throws Exception {
MockitoAnnotations.initMocks(this);
final Context context = InstrumentationRegistry.getContext();
setCurrentTimeMillis(TEST_START);
registerLocalServices();
// Intercept various broadcasts, and pretend that uids have packages.
// Also return mock service instances for a few critical services.
mServiceContext = new BroadcastInterceptingContext(context) {
@Override
public PackageManager getPackageManager() {
return mPackageManager;
}
@Override
public void startActivity(Intent intent) {
// ignored
}
@Override
public Object getSystemService(String name) {
switch (name) {
case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
return mSubscriptionManager;
case Context.CARRIER_CONFIG_SERVICE:
return mCarrierConfigManager;
case Context.TELEPHONY_SERVICE:
return mTelephonyManager;
case Context.NOTIFICATION_SERVICE:
return mNotifManager;
default:
return super.getSystemService(name);
}
}
@Override
public void enforceCallingOrSelfPermission(String permission, String message) {
// Assume that we're AID_SYSTEM
}
};
setNetpolicyXml(context);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
mUidObserver = (IUidObserver) invocation.getArguments()[0];
Log.d(TAG, "set mUidObserver to " + mUidObserver);
return null;
}
}).when(mActivityManager).registerUidObserver(any(), anyInt(),
eq(ActivityManager.PROCESS_STATE_UNKNOWN), isNull(String.class));
mFutureIntent = newRestrictBackgroundChangedFuture();
mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
mNetworkManager, mIpm, mClock, mPolicyDir, true);
mService.bindConnectivityManager(mConnManager);
mPolicyListener = new NetworkPolicyListenerAnswer(mService);
// Sets some common expectations.
when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenAnswer(
new Answer<PackageInfo>() {
@Override
public PackageInfo answer(InvocationOnMock invocation) throws Throwable {
final String packageName = (String) invocation.getArguments()[0];
final PackageInfo info = new PackageInfo();
final Signature signature;
if ("android".equals(packageName)) {
signature = new Signature("F00D");
} else {
signature = new Signature("DEAD");
}
info.signatures = new Signature[] {
signature
};
return info;
}
});
when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
.thenReturn(new ApplicationInfo());
when(mPackageManager.getPackagesForUid(UID_A)).thenReturn(new String[] {PKG_NAME_A});
when(mPackageManager.getPackagesForUid(UID_B)).thenReturn(new String[] {PKG_NAME_B});
when(mPackageManager.getPackagesForUid(UID_C)).thenReturn(new String[] {PKG_NAME_C});
when(mPackageManager.getApplicationInfo(eq(PKG_NAME_A), anyInt()))
.thenReturn(buildApplicationInfo(PKG_NAME_A));
when(mPackageManager.getApplicationInfo(eq(PKG_NAME_B), anyInt()))
.thenReturn(buildApplicationInfo(PKG_NAME_B));
when(mPackageManager.getApplicationInfo(eq(PKG_NAME_C), anyInt()))
.thenReturn(buildApplicationInfo(PKG_NAME_C));
when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true);
when(mNetworkManager.setDataSaverModeEnabled(anyBoolean())).thenReturn(true);
// Prepare NPMS.
mService.systemReady(mService.networkScoreAndNetworkManagementServiceReady());
// catch INetworkManagementEventObserver during systemReady()
final ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
verify(mNetworkManager).registerObserver(networkObserver.capture());
mNetworkObserver = networkObserver.getValue();
NetworkPolicy defaultPolicy = mService.buildDefaultMobilePolicy(0, "");
mDefaultWarningBytes = defaultPolicy.warningBytes;
mDefaultLimitBytes = defaultPolicy.limitBytes;
}
@After
public void removeFiles() throws Exception {
for (File file : mPolicyDir.listFiles()) {
file.delete();
}
}
@After
public void unregisterLocalServices() throws Exception {
// Registered by NetworkPolicyManagerService's constructor.
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
// Added in registerLocalServices()
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
LocalServices.removeServiceForTest(DeviceIdleController.LocalService.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
}
@After
public void resetClock() throws Exception {
RecurrenceRule.sClock = Clock.systemDefaultZone();
}
@Test
public void testTurnRestrictBackgroundOn() throws Exception {
assertRestrictBackgroundOff(); // Sanity check.
final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
setRestrictBackground(true);
assertRestrictBackgroundChangedReceived(futureIntent, null);
}
@Test
@NetPolicyXml("restrict-background-on.xml")
public void testTurnRestrictBackgroundOff() throws Exception {
assertRestrictBackgroundOn(); // Sanity check.
assertRestrictBackgroundChangedReceived(mFutureIntent, null);
final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
setRestrictBackground(false);
assertRestrictBackgroundChangedReceived(futureIntent, null);
}
/**
* Adds whitelist when restrict background is on - app should receive an intent.
*/
@Test
@NetPolicyXml("restrict-background-on.xml")
public void testAddRestrictBackgroundWhitelist_restrictBackgroundOn() throws Exception {
assertRestrictBackgroundOn(); // Sanity check.
assertRestrictBackgroundChangedReceived(mFutureIntent, null);
addRestrictBackgroundWhitelist(true);
}
/**
* Adds whitelist when restrict background is off - app should not receive an intent.
*/
@Test
public void testAddRestrictBackgroundWhitelist_restrictBackgroundOff() throws Exception {
assertRestrictBackgroundOff(); // Sanity check.
addRestrictBackgroundWhitelist(false);
}
private void addRestrictBackgroundWhitelist(boolean expectIntent) throws Exception {
// Sanity checks.
assertWhitelistUids();
assertUidPolicy(UID_A, POLICY_NONE);
final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
mPolicyListener.expect().onUidPoliciesChanged(anyInt(), anyInt());
mService.setUidPolicy(UID_A, POLICY_ALLOW_METERED_BACKGROUND);
assertWhitelistUids(UID_A);
assertUidPolicy(UID_A, POLICY_ALLOW_METERED_BACKGROUND);
mPolicyListener.waitAndVerify()
.onUidPoliciesChanged(APP_ID_A, POLICY_ALLOW_METERED_BACKGROUND);
if (expectIntent) {
assertRestrictBackgroundChangedReceived(futureIntent, PKG_NAME_A);
} else {
futureIntent.assertNotReceived();
}
}
/**
* Removes whitelist when restrict background is on - app should receive an intent.
*/
@Test
@NetPolicyXml("uidA-whitelisted-restrict-background-on.xml")
public void testRemoveRestrictBackgroundWhitelist_restrictBackgroundOn() throws Exception {
assertRestrictBackgroundOn(); // Sanity check.
assertRestrictBackgroundChangedReceived(mFutureIntent, null);
removeRestrictBackgroundWhitelist(true);
}
/**
* Removes whitelist when restrict background is off - app should not receive an intent.
*/
@Test
@NetPolicyXml("uidA-whitelisted-restrict-background-off.xml")
public void testRemoveRestrictBackgroundWhitelist_restrictBackgroundOff() throws Exception {
assertRestrictBackgroundOff(); // Sanity check.
removeRestrictBackgroundWhitelist(false);
}
@Test
public void testLowPowerModeObserver_ListenersRegistered()
throws Exception {
PowerManagerInternal pmInternal = LocalServices.getService(PowerManagerInternal.class);
verify(pmInternal, atLeast(2)).registerLowPowerModeObserver(any());
}
@Test
public void updateRestrictBackgroundByLowPowerMode_RestrictOnBeforeBsm_RestrictOnAfterBsm()
throws Exception {
setRestrictBackground(true);
PowerSaveState stateOn = new PowerSaveState.Builder()
.setGlobalBatterySaverEnabled(true)
.setBatterySaverEnabled(false)
.build();
mService.updateRestrictBackgroundByLowPowerModeUL(stateOn);
// RestrictBackground should be on even though battery saver want to turn it off
assertTrue(mService.getRestrictBackground());
PowerSaveState stateOff = new PowerSaveState.Builder()
.setGlobalBatterySaverEnabled(false)
.setBatterySaverEnabled(false)
.build();
mService.updateRestrictBackgroundByLowPowerModeUL(stateOff);
// RestrictBackground should be on, following its previous state
assertTrue(mService.getRestrictBackground());
}
@Test
public void updateRestrictBackgroundByLowPowerMode_RestrictOffBeforeBsm_RestrictOffAfterBsm()
throws Exception {
setRestrictBackground(false);
PowerSaveState stateOn = new PowerSaveState.Builder()
.setGlobalBatterySaverEnabled(true)
.setBatterySaverEnabled(true)
.build();
mService.updateRestrictBackgroundByLowPowerModeUL(stateOn);
// RestrictBackground should be turned on because of battery saver
assertTrue(mService.getRestrictBackground());
PowerSaveState stateOff = new PowerSaveState.Builder()
.setGlobalBatterySaverEnabled(false)
.setBatterySaverEnabled(false)
.build();
mService.updateRestrictBackgroundByLowPowerModeUL(stateOff);
// RestrictBackground should be off, following its previous state
assertFalse(mService.getRestrictBackground());
}
@Test
public void updateRestrictBackgroundByLowPowerMode_StatusChangedInBsm_DoNotRestore()
throws Exception {
setRestrictBackground(true);
PowerSaveState stateOn = new PowerSaveState.Builder()
.setGlobalBatterySaverEnabled(true)
.setBatterySaverEnabled(true)
.build();
mService.updateRestrictBackgroundByLowPowerModeUL(stateOn);
// RestrictBackground should still be on
assertTrue(mService.getRestrictBackground());
// User turns off RestrictBackground manually
setRestrictBackground(false);
PowerSaveState stateOff = new PowerSaveState.Builder().setBatterySaverEnabled(
false).build();
mService.updateRestrictBackgroundByLowPowerModeUL(stateOff);
// RestrictBackground should be off because user changes it manually
assertFalse(mService.getRestrictBackground());
}
private void removeRestrictBackgroundWhitelist(boolean expectIntent) throws Exception {
// Sanity checks.
assertWhitelistUids(UID_A);
assertUidPolicy(UID_A, POLICY_ALLOW_METERED_BACKGROUND);
final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
mPolicyListener.expect().onUidPoliciesChanged(anyInt(), anyInt());
mService.setUidPolicy(UID_A, POLICY_NONE);
assertWhitelistUids();
assertUidPolicy(UID_A, POLICY_NONE);
mPolicyListener.waitAndVerify().onUidPoliciesChanged(APP_ID_A, POLICY_NONE);
if (expectIntent) {
assertRestrictBackgroundChangedReceived(futureIntent, PKG_NAME_A);
} else {
futureIntent.assertNotReceived();
}
}
/**
* Adds blacklist when restrict background is on - app should not receive an intent.
*/
@Test
@NetPolicyXml("restrict-background-on.xml")
public void testAddRestrictBackgroundBlacklist_restrictBackgroundOn() throws Exception {
assertRestrictBackgroundOn(); // Sanity check.
assertRestrictBackgroundChangedReceived(mFutureIntent, null);
addRestrictBackgroundBlacklist(false);
}
/**
* Adds blacklist when restrict background is off - app should receive an intent.
*/
@Test
public void testAddRestrictBackgroundBlacklist_restrictBackgroundOff() throws Exception {
assertRestrictBackgroundOff(); // Sanity check.
addRestrictBackgroundBlacklist(true);
}
private void addRestrictBackgroundBlacklist(boolean expectIntent) throws Exception {
assertUidPolicy(UID_A, POLICY_NONE); // Sanity check.
final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
mPolicyListener.expect().onUidPoliciesChanged(anyInt(), anyInt());
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
assertUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
mPolicyListener.waitAndVerify()
.onUidPoliciesChanged(APP_ID_A, POLICY_REJECT_METERED_BACKGROUND);
if (expectIntent) {
assertRestrictBackgroundChangedReceived(futureIntent, PKG_NAME_A);
} else {
futureIntent.assertNotReceived();
}
}
/**
* Removes blacklist when restrict background is on - app should not receive an intent.
*/
@Test
@NetPolicyXml("uidA-blacklisted-restrict-background-on.xml")
public void testRemoveRestrictBackgroundBlacklist_restrictBackgroundOn() throws Exception {
assertRestrictBackgroundOn(); // Sanity check.
assertRestrictBackgroundChangedReceived(mFutureIntent, null);
removeRestrictBackgroundBlacklist(false);
}
/**
* Removes blacklist when restrict background is off - app should receive an intent.
*/
@Test
@NetPolicyXml("uidA-blacklisted-restrict-background-off.xml")
public void testRemoveRestrictBackgroundBlacklist_restrictBackgroundOff() throws Exception {
assertRestrictBackgroundOff(); // Sanity check.
removeRestrictBackgroundBlacklist(true);
}
private void removeRestrictBackgroundBlacklist(boolean expectIntent) throws Exception {
assertUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND); // Sanity check.
final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
mPolicyListener.expect().onUidPoliciesChanged(anyInt(), anyInt());
mService.setUidPolicy(UID_A, POLICY_NONE);
assertUidPolicy(UID_A, POLICY_NONE);
mPolicyListener.waitAndVerify()
.onUidPoliciesChanged(APP_ID_A, POLICY_NONE);
if (expectIntent) {
assertRestrictBackgroundChangedReceived(futureIntent, PKG_NAME_A);
} else {
futureIntent.assertNotReceived();
}
}
@Test
@NetPolicyXml("uidA-blacklisted-restrict-background-on.xml")
public void testBlacklistedAppIsNotNotifiedWhenRestrictBackgroundIsOn() throws Exception {
// Sanity checks.
assertRestrictBackgroundOn();
assertRestrictBackgroundChangedReceived(mFutureIntent, null);
assertUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
setRestrictBackground(true);
futureIntent.assertNotReceived();
}
@Test
@NetPolicyXml("uidA-whitelisted-restrict-background-on.xml")
public void testWhitelistedAppIsNotNotifiedWhenRestrictBackgroundIsOn() throws Exception {
// Sanity checks.
assertRestrictBackgroundOn();
assertRestrictBackgroundChangedReceived(mFutureIntent, null);
assertWhitelistUids(UID_A);
final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
setRestrictBackground(true);
futureIntent.assertNotReceived();
}
@Test
@NetPolicyXml("uidA-whitelisted-restrict-background-on.xml")
public void testWhitelistedAppIsNotifiedWhenBlacklisted() throws Exception {
// Sanity checks.
assertRestrictBackgroundOn();
assertRestrictBackgroundChangedReceived(mFutureIntent, null);
assertWhitelistUids(UID_A);
final FutureIntent futureIntent = newRestrictBackgroundChangedFuture();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
assertRestrictBackgroundChangedReceived(futureIntent, PKG_NAME_A);
}
@Test
@NetPolicyXml("restrict-background-lists-whitelist-format.xml")
public void testRestrictBackgroundLists_whitelistFormat() throws Exception {
restrictBackgroundListsTest();
}
@Test
@NetPolicyXml("restrict-background-lists-uid-policy-format.xml")
public void testRestrictBackgroundLists_uidPolicyFormat() throws Exception {
restrictBackgroundListsTest();
}
private void restrictBackgroundListsTest() throws Exception {
// UIds that are whitelisted.
assertWhitelistUids(UID_A, UID_B, UID_C);
assertUidPolicy(UID_A, POLICY_ALLOW_METERED_BACKGROUND);
assertUidPolicy(UID_B, POLICY_ALLOW_METERED_BACKGROUND);
assertUidPolicy(UID_C, POLICY_ALLOW_METERED_BACKGROUND);
// UIDs that are blacklisted.
assertUidPolicy(UID_D, POLICY_NONE);
assertUidPolicy(UID_E, POLICY_REJECT_METERED_BACKGROUND);
// UIDS that have legacy policies.
assertUidPolicy(UID_F, 2); // POLICY_ALLOW_BACKGROUND_BATTERY_SAVE
// Remove whitelist.
mService.setUidPolicy(UID_A, POLICY_NONE);
assertUidPolicy(UID_A, POLICY_NONE);
assertWhitelistUids(UID_B, UID_C);
// Add whitelist when blacklisted.
mService.setUidPolicy(UID_E, POLICY_ALLOW_METERED_BACKGROUND);
assertUidPolicy(UID_E, POLICY_ALLOW_METERED_BACKGROUND);
assertWhitelistUids(UID_B, UID_C, UID_E);
// Add blacklist when whitelisted.
mService.setUidPolicy(UID_B, POLICY_REJECT_METERED_BACKGROUND);
assertUidPolicy(UID_B, POLICY_REJECT_METERED_BACKGROUND);
assertWhitelistUids(UID_C, UID_E);
}
/**
* Tests scenario where an UID had {@code restrict-background} and {@code uid-policy} tags.
*/
@Test
@NetPolicyXml("restrict-background-lists-mixed-format.xml")
public void testRestrictBackgroundLists_mixedFormat() throws Exception {
assertWhitelistUids(UID_A, UID_C, UID_D);
assertUidPolicy(UID_A, POLICY_ALLOW_METERED_BACKGROUND);
assertUidPolicy(UID_B, POLICY_REJECT_METERED_BACKGROUND); // Blacklist prevails.
assertUidPolicy(UID_C, (POLICY_ALLOW_METERED_BACKGROUND | 2));
assertUidPolicy(UID_D, POLICY_ALLOW_METERED_BACKGROUND);
}
@Test
@NetPolicyXml("uids-with-mixed-policies.xml")
public void testGetUidsWithPolicy() throws Exception {
assertContainsInAnyOrder(mService.getUidsWithPolicy(POLICY_NONE));
assertContainsInAnyOrder(mService.getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND),
UID_B, UID_D);
assertContainsInAnyOrder(mService.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND),
UID_E, UID_F);
// Legacy (POLICY_ALLOW_BACKGROUND_BATTERY_SAVE)
assertContainsInAnyOrder(mService.getUidsWithPolicy(2),
UID_C, UID_D, UID_F);
}
// NOTE: testPolicyChangeTriggersListener() is too superficial, they
// don't check for side-effects (like calls to NetworkManagementService) neither cover all
// different modes (Data Saver, Battery Saver, Doze, App idle, etc...).
// These scenarios are extensively tested on CTS' HostsideRestrictBackgroundNetworkTests.
@Test
public void testUidForeground() throws Exception {
// push all uids into background
callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, 0);
callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_SERVICE, 0);
assertFalse(mService.isUidForeground(UID_A));
assertFalse(mService.isUidForeground(UID_B));
// push one of the uids into foreground
callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_TOP, 0);
assertTrue(mService.isUidForeground(UID_A));
assertFalse(mService.isUidForeground(UID_B));
// and swap another uid into foreground
callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, 0);
callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_TOP, 0);
assertFalse(mService.isUidForeground(UID_A));
assertTrue(mService.isUidForeground(UID_B));
}
private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
ZoneId.systemDefault());
final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = policy.cycleIterator();
while (it.hasNext()) {
final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
if (cycle.first.toInstant().toEpochMilli() < currentTime) {
return cycle.first.toInstant().toEpochMilli();
}
}
throw new IllegalStateException(
"Failed to find current cycle for " + policy + " at " + currentTime);
}
private static long computeNextCycleBoundary(long currentTime, NetworkPolicy policy) {
RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
ZoneId.systemDefault());
return policy.cycleIterator().next().second.toInstant().toEpochMilli();
}
@Test
public void testLastCycleBoundaryThisMonth() throws Exception {
// assume cycle day of "5th", which should be in same month
final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-11-05T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 5, TIMEZONE_UTC, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
@Test
public void testLastCycleBoundaryLastMonth() throws Exception {
// assume cycle day of "20th", which should be in last month
final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-10-20T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 20, TIMEZONE_UTC, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
@Test
public void testLastCycleBoundaryThisMonthFebruary() throws Exception {
// assume cycle day of "30th" in february; should go to january
final long currentTime = parseTime("2007-02-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-01-30T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 30, TIMEZONE_UTC, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
@Test
public void testLastCycleBoundaryLastMonthFebruary() throws Exception {
// assume cycle day of "30th" in february, which should clamp
final long currentTime = parseTime("2007-03-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-02-28T23:59:59.999Z");
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 30, TIMEZONE_UTC, 1024L, 1024L, false);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertTimeEquals(expectedCycle, actualCycle);
}
@Test
public void testCycleBoundaryLeapYear() throws Exception {
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 29, TIMEZONE_UTC, 1024L, 1024L, false);
assertTimeEquals(parseTime("2012-01-29T00:00:00.000Z"),
computeNextCycleBoundary(parseTime("2012-01-14T00:00:00.000Z"), policy));
assertTimeEquals(parseTime("2012-02-29T00:00:00.000Z"),
computeNextCycleBoundary(parseTime("2012-02-14T00:00:00.000Z"), policy));
assertTimeEquals(parseTime("2012-02-29T00:00:00.000Z"),
computeLastCycleBoundary(parseTime("2012-03-14T00:00:00.000Z"), policy));
assertTimeEquals(parseTime("2012-03-29T00:00:00.000Z"),
computeNextCycleBoundary(parseTime("2012-03-14T00:00:00.000Z"), policy));
assertTimeEquals(parseTime("2007-01-29T00:00:00.000Z"),
computeNextCycleBoundary(parseTime("2007-01-14T00:00:00.000Z"), policy));
assertTimeEquals(parseTime("2007-02-28T23:59:59.999Z"),
computeNextCycleBoundary(parseTime("2007-02-14T00:00:00.000Z"), policy));
assertTimeEquals(parseTime("2007-02-28T23:59:59.999Z"),
computeLastCycleBoundary(parseTime("2007-03-14T00:00:00.000Z"), policy));
assertTimeEquals(parseTime("2007-03-29T00:00:00.000Z"),
computeNextCycleBoundary(parseTime("2007-03-14T00:00:00.000Z"), policy));
}
@Test
public void testNextCycleTimezoneAfterUtc() throws Exception {
// US/Central is UTC-6
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 10, "US/Central", 1024L, 1024L, false);
assertTimeEquals(parseTime("2012-01-10T06:00:00.000Z"),
computeNextCycleBoundary(parseTime("2012-01-05T00:00:00.000Z"), policy));
}
@Test
public void testNextCycleTimezoneBeforeUtc() throws Exception {
// Israel is UTC+2
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 10, "Israel", 1024L, 1024L, false);
assertTimeEquals(parseTime("2012-01-09T22:00:00.000Z"),
computeNextCycleBoundary(parseTime("2012-01-05T00:00:00.000Z"), policy));
}
@Test
public void testCycleTodayJanuary() throws Exception {
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 14, "US/Pacific", 1024L, 1024L, false);
assertTimeEquals(parseTime("2013-01-14T00:00:00.000-08:00"),
computeNextCycleBoundary(parseTime("2013-01-13T23:59:59.000-08:00"), policy));
assertTimeEquals(parseTime("2013-02-14T00:00:00.000-08:00"),
computeNextCycleBoundary(parseTime("2013-01-14T00:00:01.000-08:00"), policy));
assertTimeEquals(parseTime("2013-02-14T00:00:00.000-08:00"),
computeNextCycleBoundary(parseTime("2013-01-14T15:11:00.000-08:00"), policy));
assertTimeEquals(parseTime("2012-12-14T00:00:00.000-08:00"),
computeLastCycleBoundary(parseTime("2013-01-13T23:59:59.000-08:00"), policy));
assertTimeEquals(parseTime("2013-01-14T00:00:00.000-08:00"),
computeLastCycleBoundary(parseTime("2013-01-14T00:00:01.000-08:00"), policy));
assertTimeEquals(parseTime("2013-01-14T00:00:00.000-08:00"),
computeLastCycleBoundary(parseTime("2013-01-14T15:11:00.000-08:00"), policy));
}
@Test
public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
NetworkState[] state = null;
NetworkStats stats = null;
final int CYCLE_DAY = 15;
final long NOW = parseTime("2007-03-10T00:00Z");
final long CYCLE_START = parseTime("2007-02-15T00:00Z");
final long CYCLE_END = parseTime("2007-03-15T00:00Z");
setCurrentTimeMillis(NOW);
// first, pretend that wifi network comes online. no policy active,
// which means we shouldn't push limit to interface.
state = new NetworkState[] { buildWifi() };
when(mConnManager.getAllNetworkState()).thenReturn(state);
mPolicyListener.expect().onMeteredIfacesChanged(any());
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
mPolicyListener.waitAndVerify().onMeteredIfacesChanged(any());
// now change cycle to be on 15th, and test in early march, to verify we
// pick cycle day in previous month.
when(mConnManager.getAllNetworkState()).thenReturn(state);
// pretend that 512 bytes total have happened
stats = new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
.thenReturn(stats.getTotalBytes());
mPolicyListener.expect().onMeteredIfacesChanged(any());
setNetworkPolicies(new NetworkPolicy(
sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
(2 * MB_IN_BYTES) - 512);
}
@Test
public void testNotificationWarningLimitSnooze() throws Exception {
// Create a place to store fake usage
final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
.thenAnswer(new Answer<Long>() {
@Override
public Long answer(InvocationOnMock invocation) throws Throwable {
final NetworkStatsHistory.Entry entry = history.getValues(
invocation.getArgument(1), invocation.getArgument(2), null);
return entry.rxBytes + entry.txBytes;
}
});
when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
.thenAnswer(new Answer<NetworkStats>() {
@Override
public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
return stats;
}
});
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
// Define simple data plan
final SubscriptionPlan plan = SubscriptionPlan.Builder
.createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
.setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED)
.build();
mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
mServiceContext.getOpPackageName());
// We're 20% through the month (6 days)
final long start = parseTime("2015-11-01T00:00Z");
final long end = parseTime("2015-11-07T00:00Z");
setCurrentTimeMillis(end);
// Normal usage means no notification
{
history.clear();
history.recordData(start, end,
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
reset(mTelephonyManager, mNetworkManager, mNotifManager);
expectMobileDefaults();
mService.updateNetworks();
verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
DataUnit.MEGABYTES.toBytes(1800 - 360));
verify(mNotifManager, never()).notifyAsUser(any(), anyInt(), any(), any());
}
// Push over warning
{
history.clear();
history.recordData(start, end,
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
reset(mTelephonyManager, mNetworkManager, mNotifManager);
expectMobileDefaults();
mService.updateNetworks();
verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
DataUnit.MEGABYTES.toBytes(1800 - 1799));
verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_WARNING),
isA(Notification.class), eq(UserHandle.ALL));
}
// Push over limit
{
history.clear();
history.recordData(start, end,
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1810), 0L, 0L, 0L, 0));
reset(mTelephonyManager, mNetworkManager, mNotifManager);
expectMobileDefaults();
mService.updateNetworks();
verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(false, TEST_SUB_ID);
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE, 1);
verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_LIMIT),
isA(Notification.class), eq(UserHandle.ALL));
}
// Snooze limit
{
reset(mTelephonyManager, mNetworkManager, mNotifManager);
expectMobileDefaults();
mService.snoozeLimit(NetworkTemplate.buildTemplateMobileAll(TEST_IMSI));
mService.updateNetworks();
verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
Long.MAX_VALUE);
verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_LIMIT_SNOOZED),
isA(Notification.class), eq(UserHandle.ALL));
}
}
@Test
public void testNotificationRapid() throws Exception {
// Create a place to store fake usage
final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
.thenAnswer(new Answer<Long>() {
@Override
public Long answer(InvocationOnMock invocation) throws Throwable {
final NetworkStatsHistory.Entry entry = history.getValues(
invocation.getArgument(1), invocation.getArgument(2), null);
return entry.rxBytes + entry.txBytes;
}
});
when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
.thenAnswer(new Answer<NetworkStats>() {
@Override
public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
return stats;
}
});
// Get active mobile network in place
expectMobileDefaults();
mService.updateNetworks();
// Define simple data plan which gives us effectively 60MB/day
final SubscriptionPlan plan = SubscriptionPlan.Builder
.createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
.setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED)
.build();
mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
mServiceContext.getOpPackageName());
// We're 20% through the month (6 days)
final long start = parseTime("2015-11-01T00:00Z");
final long end = parseTime("2015-11-07T00:00Z");
setCurrentTimeMillis(end);
// Using 20% data in 20% time is normal
{
history.clear();
history.recordData(start, end,
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
reset(mNotifManager);
mService.updateNetworks();
verify(mNotifManager, never()).notifyAsUser(any(), anyInt(), any(), any());
}
// Using 80% data in 20% time is alarming; but spread equally among
// three UIDs means we get generic alert
{
history.clear();
history.recordData(start, end,
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
stats.clear();
stats.addValues(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
stats.addValues(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
stats.addValues(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
reset(mNotifManager);
mService.updateNetworks();
final ArgumentCaptor<Notification> notif = ArgumentCaptor.forClass(Notification.class);
verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_RAPID),
notif.capture(), eq(UserHandle.ALL));
final String text = notif.getValue().extras.getCharSequence(Notification.EXTRA_TEXT)
.toString();
assertFalse(text.contains(PKG_NAME_A));
assertFalse(text.contains(PKG_NAME_B));
assertFalse(text.contains(PKG_NAME_C));
}
// Using 80% data in 20% time is alarming; but mostly done by one UID
// means we get specific alert
{
history.clear();
history.recordData(start, end,
new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
stats.clear();
stats.addValues(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
stats.addValues(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
reset(mNotifManager);
mService.updateNetworks();
final ArgumentCaptor<Notification> notif = ArgumentCaptor.forClass(Notification.class);
verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_RAPID),
notif.capture(), eq(UserHandle.ALL));
final String text = notif.getValue().extras.getCharSequence(Notification.EXTRA_TEXT)
.toString();
assertTrue(text.contains(PKG_NAME_A));
assertFalse(text.contains(PKG_NAME_B));
assertFalse(text.contains(PKG_NAME_C));
}
}
@Test
public void testMeteredNetworkWithoutLimit() throws Exception {
NetworkState[] state = null;
NetworkStats stats = null;
final long TIME_FEB_15 = 1171497600000L;
final long TIME_MAR_10 = 1173484800000L;
final int CYCLE_DAY = 15;
setCurrentTimeMillis(TIME_MAR_10);
// bring up wifi network with metered policy
state = new NetworkState[] { buildWifi() };
stats = new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
{
when(mConnManager.getAllNetworkState()).thenReturn(state);
when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
currentTimeMillis())).thenReturn(stats.getTotalBytes());
mPolicyListener.expect().onMeteredIfacesChanged(any());
setNetworkPolicies(new NetworkPolicy(
sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, WARNING_DISABLED, LIMIT_DISABLED,
true));
mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
Long.MAX_VALUE);
}
}
@Test
public void testOnUidStateChanged_notifyAMS() throws Exception {
final long procStateSeq = 222;
callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq);
verify(mActivityManagerInternal).notifyNetworkPolicyRulesUpdated(UID_A, procStateSeq);
}
private void callOnUidStateChanged(int uid, int procState, long procStateSeq)
throws Exception {
mUidObserver.onUidStateChanged(uid, procState, procStateSeq);
final CountDownLatch latch = new CountDownLatch(1);
mService.mUidEventHandler.post(() -> {
latch.countDown();
});
latch.await(2, TimeUnit.SECONDS);
}
private void assertCycleDayAsExpected(PersistableBundle config, int carrierCycleDay,
boolean expectValid) {
config.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, carrierCycleDay);
int actualCycleDay = mService.getCycleDayFromCarrierConfig(config,
INVALID_CARRIER_CONFIG_VALUE);
if (expectValid) {
assertEquals(carrierCycleDay, actualCycleDay);
} else {
// INVALID_CARRIER_CONFIG_VALUE is returned for invalid values
assertEquals(INVALID_CARRIER_CONFIG_VALUE, actualCycleDay);
}
}
@Test
public void testGetCycleDayFromCarrierConfig() {
PersistableBundle config = CarrierConfigManager.getDefaultConfig();
final Calendar cal = Calendar.getInstance();
int actualCycleDay;
config.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, DATA_CYCLE_USE_PLATFORM_DEFAULT);
actualCycleDay = mService.getCycleDayFromCarrierConfig(config, DEFAULT_CYCLE_DAY);
assertEquals(DEFAULT_CYCLE_DAY, actualCycleDay);
// null config returns a default value
actualCycleDay = mService.getCycleDayFromCarrierConfig(null, DEFAULT_CYCLE_DAY);
assertEquals(DEFAULT_CYCLE_DAY, actualCycleDay);
// Sane, non-default values
assertCycleDayAsExpected(config, 1, true);
assertCycleDayAsExpected(config, cal.getMaximum(Calendar.DAY_OF_MONTH), true);
assertCycleDayAsExpected(config, cal.getMinimum(Calendar.DAY_OF_MONTH), true);
// Invalid values
assertCycleDayAsExpected(config, 0, false);
assertCycleDayAsExpected(config, DATA_CYCLE_THRESHOLD_DISABLED, false);
assertCycleDayAsExpected(config, cal.getMaximum(Calendar.DAY_OF_MONTH) + 1, false);
assertCycleDayAsExpected(config, cal.getMinimum(Calendar.DAY_OF_MONTH) - 5, false);
}
private void assertWarningBytesAsExpected(PersistableBundle config, long carrierWarningBytes,
long expected) {
config.putLong(KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, carrierWarningBytes);
long actualWarning = mService.getWarningBytesFromCarrierConfig(config,
INVALID_CARRIER_CONFIG_VALUE);
assertEquals(expected, actualWarning);
}
@Test
public void testGetWarningBytesFromCarrierConfig() {
PersistableBundle config = CarrierConfigManager.getDefaultConfig();
long actualWarningBytes;
assertWarningBytesAsExpected(config, DATA_CYCLE_USE_PLATFORM_DEFAULT,
mDefaultWarningBytes);
assertWarningBytesAsExpected(config, DATA_CYCLE_THRESHOLD_DISABLED, WARNING_DISABLED);
assertWarningBytesAsExpected(config, 0, 0);
// not a valid value
assertWarningBytesAsExpected(config, -1000, INVALID_CARRIER_CONFIG_VALUE);
// null config returns a default value
actualWarningBytes = mService.getWarningBytesFromCarrierConfig(null, mDefaultWarningBytes);
assertEquals(mDefaultWarningBytes, actualWarningBytes);
}
private void assertLimitBytesAsExpected(PersistableBundle config, long carrierWarningBytes,
long expected) {
config.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, carrierWarningBytes);
long actualWarning = mService.getLimitBytesFromCarrierConfig(config,
INVALID_CARRIER_CONFIG_VALUE);
assertEquals(expected, actualWarning);
}
@Test
public void testGetLimitBytesFromCarrierConfig() {
PersistableBundle config = CarrierConfigManager.getDefaultConfig();
long actualLimitBytes;
assertLimitBytesAsExpected(config, DATA_CYCLE_USE_PLATFORM_DEFAULT,
mDefaultLimitBytes);
assertLimitBytesAsExpected(config, DATA_CYCLE_THRESHOLD_DISABLED, LIMIT_DISABLED);
assertLimitBytesAsExpected(config, 0, 0);
// not a valid value
assertLimitBytesAsExpected(config, -1000, INVALID_CARRIER_CONFIG_VALUE);
// null config returns a default value
actualLimitBytes = mService.getWarningBytesFromCarrierConfig(null, mDefaultLimitBytes);
assertEquals(mDefaultLimitBytes, actualLimitBytes);
}
private PersistableBundle setupUpdateMobilePolicyCycleTests() throws RemoteException {
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
PersistableBundle bundle = CarrierConfigManager.getDefaultConfig();
when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(bundle);
setNetworkPolicies(buildDefaultFakeMobilePolicy());
return bundle;
}
@Test
public void testUpdateMobilePolicyCycleWithNullConfig() throws RemoteException {
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(null);
setNetworkPolicies(buildDefaultFakeMobilePolicy());
// smoke test to make sure no errors are raised
mServiceContext.sendBroadcast(
new Intent(ACTION_CARRIER_CONFIG_CHANGED)
.putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
);
assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes,
true);
}
@Test
public void testUpdateMobilePolicyCycleWithInvalidConfig() throws RemoteException {
PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
// Test with an invalid CarrierConfig, there should be no changes or crashes.
bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, -100);
bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, -100);
bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, -100);
mServiceContext.sendBroadcast(
new Intent(ACTION_CARRIER_CONFIG_CHANGED)
.putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
);
assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes,
true);
}
@Test
public void testUpdateMobilePolicyCycleWithDefaultConfig() throws RemoteException {
PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
// Test that we respect the platform values when told to
bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT,
DATA_CYCLE_USE_PLATFORM_DEFAULT);
bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG,
DATA_CYCLE_USE_PLATFORM_DEFAULT);
bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG,
DATA_CYCLE_USE_PLATFORM_DEFAULT);
mServiceContext.sendBroadcast(
new Intent(ACTION_CARRIER_CONFIG_CHANGED)
.putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
);
assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes,
true);
}
@Test
public void testUpdateMobilePolicyCycleWithUserOverrides() throws RemoteException {
PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
// inferred = false implies that a user manually modified this policy.
NetworkPolicy policy = buildDefaultFakeMobilePolicy();
policy.inferred = false;
setNetworkPolicies(policy);
bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31);
bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, 9999);
bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG,
DATA_CYCLE_THRESHOLD_DISABLED);
mServiceContext.sendBroadcast(
new Intent(ACTION_CARRIER_CONFIG_CHANGED)
.putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
);
// The policy still shouldn't change, because we don't want to overwrite user settings.
assertNetworkPolicyEquals(DEFAULT_CYCLE_DAY, mDefaultWarningBytes, mDefaultLimitBytes,
false);
}
@Test
public void testUpdateMobilePolicyCycleUpdatesDataCycle() throws RemoteException {
PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31);
bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, 9999);
bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, 9999);
mServiceContext.sendBroadcast(
new Intent(ACTION_CARRIER_CONFIG_CHANGED)
.putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
);
assertNetworkPolicyEquals(31, 9999, 9999, true);
}
@Test
public void testUpdateMobilePolicyCycleDisableThresholds() throws RemoteException {
PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31);
bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG,
DATA_CYCLE_THRESHOLD_DISABLED);
bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG,
DATA_CYCLE_THRESHOLD_DISABLED);
mServiceContext.sendBroadcast(
new Intent(ACTION_CARRIER_CONFIG_CHANGED)
.putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
);
assertNetworkPolicyEquals(31, WARNING_DISABLED, LIMIT_DISABLED, true);
}
@Test
public void testUpdateMobilePolicyCycleRevertsToDefault() throws RemoteException {
PersistableBundle bundle = setupUpdateMobilePolicyCycleTests();
bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT, 31);
bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG,
DATA_CYCLE_THRESHOLD_DISABLED);
bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG,
DATA_CYCLE_THRESHOLD_DISABLED);
mServiceContext.sendBroadcast(
new Intent(ACTION_CARRIER_CONFIG_CHANGED)
.putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
);
assertNetworkPolicyEquals(31, WARNING_DISABLED, LIMIT_DISABLED, true);
// If the user switches carriers to one that doesn't use a CarrierConfig, we should revert
// to the default data limit and warning. The cycle date doesn't need to revert as it's
// arbitrary anyways.
bundle.putInt(CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT,
DATA_CYCLE_USE_PLATFORM_DEFAULT);
bundle.putLong(CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG,
DATA_CYCLE_USE_PLATFORM_DEFAULT);
bundle.putLong(CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG,
DATA_CYCLE_USE_PLATFORM_DEFAULT);
mServiceContext.sendBroadcast(
new Intent(ACTION_CARRIER_CONFIG_CHANGED)
.putExtra(PhoneConstants.SUBSCRIPTION_KEY, FAKE_SUB_ID)
);
assertNetworkPolicyEquals(31, mDefaultWarningBytes, mDefaultLimitBytes,
true);
}
private ApplicationInfo buildApplicationInfo(String label) {
final ApplicationInfo ai = new ApplicationInfo();
ai.nonLocalizedLabel = label;
return ai;
}
private NetworkInfo buildNetworkInfo() {
final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
TelephonyManager.NETWORK_TYPE_LTE, null, null);
ni.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
return ni;
}
private LinkProperties buildLinkProperties(String iface) {
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(iface);
return lp;
}
private NetworkCapabilities buildNetworkCapabilities(int subId) {
final NetworkCapabilities nc = new NetworkCapabilities();
nc.addTransportType(TRANSPORT_CELLULAR);
nc.setNetworkSpecifier(new StringNetworkSpecifier(String.valueOf(subId)));
return nc;
}
private NetworkPolicy buildDefaultFakeMobilePolicy() {
NetworkPolicy p = mService.buildDefaultMobilePolicy(FAKE_SUB_ID, FAKE_SUBSCRIBER_ID);
// set a deterministic cycle date
p.cycleRule = new RecurrenceRule(
p.cycleRule.start.withDayOfMonth(DEFAULT_CYCLE_DAY),
p.cycleRule.end, Period.ofMonths(1));
return p;
}
private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes,
long limitBytes, boolean inferred){
final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID);
return new NetworkPolicy(template, cycleDay, new Time().timezone, warningBytes,
limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred);
}
private void assertNetworkPolicyEquals(int expectedCycleDay, long expectedWarningBytes,
long expectedLimitBytes, boolean expectedInferred) {
NetworkPolicy[] policies = mService.getNetworkPolicies(
mServiceContext.getOpPackageName());
assertEquals("Unexpected number of network policies", 1, policies.length);
NetworkPolicy actualPolicy = policies[0];
NetworkPolicy expectedPolicy = buildFakeMobilePolicy(expectedCycleDay, expectedWarningBytes,
expectedLimitBytes, expectedInferred);
assertEquals(expectedPolicy, actualPolicy);
}
private static long parseTime(String time) {
return ZonedDateTime.parse(time).toInstant().toEpochMilli();
}
private void setNetworkPolicies(NetworkPolicy... policies) {
mService.setNetworkPolicies(policies);
}
private static NetworkState buildWifi() {
final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
return new NetworkState(info, prop, networkCapabilities, null, null, TEST_SSID);
}
private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception {
when(mIpm.checkUidPermission(Manifest.permission.INTERNET, uid)).thenReturn(
hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
}
private void expectMobileDefaults() throws Exception {
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(
new int[] { TEST_SUB_ID });
when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI);
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
new NetworkState(buildNetworkInfo(),
buildLinkProperties(TEST_IFACE),
buildNetworkCapabilities(TEST_SUB_ID),
new Network(TEST_NET_ID), TEST_IMSI, null)
});
}
private void verifyAdvisePersistThreshold() throws Exception {
verify(mStatsService).advisePersistThreshold(anyLong());
}
private static class TestAbstractFuture<T> extends AbstractFuture<T> {
@Override
public T get() throws InterruptedException, ExecutionException {
try {
return get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
throw new RuntimeException(e);
}
}
}
private static void assertTimeEquals(long expected, long actual) {
if (expected != actual) {
fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual));
}
}
private static String formatTime(long millis) {
return Instant.ofEpochMilli(millis) + " [" + millis + "]";
}
private static void assertEqualsFuzzy(long expected, long actual, long fuzzy) {
final long low = expected - fuzzy;
final long high = expected + fuzzy;
if (actual < low || actual > high) {
fail("value " + formatTime(actual) + " is outside [" + formatTime(low) + ","
+ formatTime(high) + "]");
}
}
private static void assertUnique(LinkedHashSet<Long> seen, Long value) {
if (!seen.add(value)) {
fail("found duplicate time " + value + " in series " + seen.toString());
}
}
private static void assertNotificationType(int expected, String actualTag) {
assertEquals("notification type mismatch for '" + actualTag +"'",
Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1));
}
private void assertUidPolicy(int uid, int expected) {
final int actual = mService.getUidPolicy(uid);
if (expected != actual) {
fail("Wrong policy for UID " + uid + ": expected " + uidPoliciesToString(expected)
+ ", actual " + uidPoliciesToString(actual));
}
}
private void assertWhitelistUids(int... uids) {
assertContainsInAnyOrder(mService.getUidsWithPolicy(POLICY_ALLOW_METERED_BACKGROUND), uids);
}
private void assertRestrictBackgroundOn() throws Exception {
assertTrue("restrictBackground should be set", mService.getRestrictBackground());
}
private void assertRestrictBackgroundOff() throws Exception {
assertFalse("restrictBackground should not be set", mService.getRestrictBackground());
}
private FutureIntent newRestrictBackgroundChangedFuture() {
return mServiceContext
.nextBroadcastIntent(ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED);
}
private void assertRestrictBackgroundChangedReceived(Future<Intent> future,
String expectedPackage) throws Exception {
final String action = ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
final Intent intent = future.get(5, TimeUnit.SECONDS);
assertNotNull("Didn't get a " + action + "intent in 5 seconds");
assertEquals("Wrong package on " + action + " intent", expectedPackage, intent.getPackage());
}
// TODO: replace by Truth, Hamcrest, or a similar tool.
private void assertContainsInAnyOrder(int[] actual, int...expected) {
final StringBuilder errors = new StringBuilder();
if (actual.length != expected.length) {
errors.append("\tsize does not match\n");
}
final List<Integer> actualList =
Arrays.stream(actual).boxed().collect(Collectors.<Integer>toList());
final List<Integer> expectedList =
Arrays.stream(expected).boxed().collect(Collectors.<Integer>toList());
if (!actualList.containsAll(expectedList)) {
errors.append("\tmissing elements on actual list\n");
}
if (!expectedList.containsAll(actualList)) {
errors.append("\tmissing elements on expected list\n");
}
if (errors.length() > 0) {
fail("assertContainsInAnyOrder(expected=" + Arrays.toString(expected)
+ ", actual=" + Arrays.toString(actual) +") failed: \n" + errors);
}
}
private long getElapsedRealtime() {
return mElapsedRealtime;
}
private void setCurrentTimeMillis(long currentTimeMillis) {
RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTimeMillis),
ZoneId.systemDefault());
mStartTime = currentTimeMillis;
mElapsedRealtime = 0L;
}
private long currentTimeMillis() {
return mStartTime + mElapsedRealtime;
}
private void incrementCurrentTime(long duration) {
mElapsedRealtime += duration;
}
private FutureIntent mRestrictBackgroundChanged;
private void setRestrictBackground(boolean flag) throws Exception {
mService.setRestrictBackground(flag);
// Sanity check.
assertEquals("restrictBackground not set", flag, mService.getRestrictBackground());
}
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
private static <T> T addLocalServiceMock(Class<T> clazz) {
final T mock = mock(clazz);
LocalServices.addService(clazz, mock);
return mock;
}
/**
* Custom Mockito answer used to verify async {@link INetworkPolicyListener} calls.
*
* <p>Typical usage:
* <pre><code>
* mPolicyListener.expect().someCallback(any());
* // do something on objects under test
* mPolicyListener.waitAndVerify().someCallback(eq(expectedValue));
* </code></pre>
*/
final class NetworkPolicyListenerAnswer implements Answer<Void> {
private CountDownLatch latch;
private final INetworkPolicyListener listener;
NetworkPolicyListenerAnswer(NetworkPolicyManagerService service) {
this.listener = mock(INetworkPolicyListener.class);
// RemoteCallbackList needs a binder to use as key
when(listener.asBinder()).thenReturn(new Binder());
service.registerListener(listener);
}
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Log.d(TAG,"counting down on answer: " + invocation);
latch.countDown();
return null;
}
INetworkPolicyListener expect() {
assertNull("expect() called before waitAndVerify()", latch);
latch = new CountDownLatch(1);
return doAnswer(this).when(listener);
}
INetworkPolicyListener waitAndVerify() {
assertNotNull("waitAndVerify() called before expect()", latch);
try {
assertTrue("callback not called in 5 seconds", latch.await(5, TimeUnit.SECONDS));
} catch (InterruptedException e) {
fail("Thread interrupted before callback called");
} finally {
latch = null;
}
return verify(listener, atLeastOnce());
}
INetworkPolicyListener verifyNotCalled() {
return verify(listener, never());
}
}
private void setNetpolicyXml(Context context) throws Exception {
mPolicyDir = context.getFilesDir();
if (mPolicyDir.exists()) {
IoUtils.deleteContents(mPolicyDir);
}
if (!TextUtils.isEmpty(mNetpolicyXml)) {
final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml;
final File netConfigFile = new File(mPolicyDir, "netpolicy.xml");
Log.d(TAG, "Creating " + netConfigFile + " from asset " + assetPath);
try (final InputStream in = context.getResources().getAssets().open(assetPath);
final OutputStream out = new FileOutputStream(netConfigFile)) {
Streams.copy(in, out);
}
}
}
/**
* Annotation used to define the relative path of the {@code netpolicy.xml} file.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface NetPolicyXml {
public String value() default "";
}
/**
* Rule used to set {@code mNetPolicyXml} according to the {@link NetPolicyXml} annotation.
*/
public static class NetPolicyMethodRule implements MethodRule {
@Override
public Statement apply(Statement base, FrameworkMethod method, Object target) {
for (Annotation annotation : method.getAnnotations()) {
if ((annotation instanceof NetPolicyXml)) {
final String path = ((NetPolicyXml) annotation).value();
if (!path.isEmpty()) {
((NetworkPolicyManagerServiceTest) target).mNetpolicyXml = path;
break;
}
}
}
return base;
}
}
}